mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-08-10 14:59:10 +00:00
v3.9.2 前端开放online源码
This commit is contained in:
parent
4e03c2c80a
commit
11b1ab81d7
125
jeecgboot-vue3/src/views/super/online/cgform/CgformCopyList.vue
Normal file
125
jeecgboot-vue3/src/views/super/online/cgform/CgformCopyList.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div :class="prefixCls">
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #tableTitle>
|
||||
<a-button @click="onShowCustomButton" type="primary" preIcon="ant-design:highlight">自定义按钮</a-button>
|
||||
<a-button @click="onShowEnhanceJs" type="primary" preIcon="ant-design:strikethrough">JS增强</a-button>
|
||||
<a-button @click="onShowEnhanceSql" type="primary" preIcon="ant-design:filter">SQL增强</a-button>
|
||||
<a-button @click="onShowEnhanceJava" type="primary" preIcon="ant-design:tool">Java增强</a-button>
|
||||
</template>
|
||||
|
||||
<template #dbSync="{ text }">
|
||||
<span v-if="text === 'Y'" style="color: limegreen">已同步</span>
|
||||
<span v-if="text === 'N'" style="color: red">未同步</span>
|
||||
</template>
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
<CgformModal @register="registerCgformModal" :actionButton="false" @success="reload" />
|
||||
<EnhanceJsModal @register="registerEnhanceJsModal" />
|
||||
<EnhanceJavaModal @register="registerEnhanceJavaModal" />
|
||||
<EnhanceSqlModal @register="registerEnhanceSqlModal" />
|
||||
<DbToOnlineModal @register="registerDbToOnlineModal" @success="reload" />
|
||||
<CustomButtonList @register="registerCustomButtonModal" />
|
||||
<AuthManagerDrawer @register="registerAuthManagerDrawer" />
|
||||
<AuthSetterModal @register="registerAuthSetterModal" />
|
||||
<CgformAddressModal @register="registerAddressModal" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { watch, provide, defineComponent } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import CgformModal from './components/CgformModal.vue';
|
||||
import DbToOnlineModal from './components/DbToOnlineModal.vue';
|
||||
import CustomButtonList from './components/button/CustomButtonList.vue';
|
||||
import EnhanceJsModal from './components/enhance/EnhanceJsModal.vue';
|
||||
import EnhanceJavaModal from './components/enhance/EnhanceJavaModal.vue';
|
||||
import EnhanceSqlModal from './components/enhance/EnhanceSqlModal.vue';
|
||||
import AuthManagerDrawer from './components/auth/AuthManagerDrawer.vue';
|
||||
import AuthSetterModal from './components/auth/AuthSetterModal.vue';
|
||||
import CgformAddressModal from "./components/CgformAddressModal.vue";
|
||||
import { useCgformList } from './hooks/useCgformList';
|
||||
import { CgformPageType } from './types';
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
export default defineComponent({
|
||||
name: 'CgformCopyList',
|
||||
components: {
|
||||
BasicTable,
|
||||
TableAction,
|
||||
CgformModal,
|
||||
DbToOnlineModal,
|
||||
CustomButtonList,
|
||||
EnhanceJsModal,
|
||||
EnhanceJavaModal,
|
||||
EnhanceSqlModal,
|
||||
AuthManagerDrawer,
|
||||
AuthSetterModal,
|
||||
CgformAddressModal,
|
||||
},
|
||||
setup() {
|
||||
const pageType = CgformPageType.copy;
|
||||
provide('cgformPageType', pageType);
|
||||
const {
|
||||
router,
|
||||
pageContext,
|
||||
getTableAction,
|
||||
getDropDownAction,
|
||||
onShowCustomButton,
|
||||
onShowEnhanceJs,
|
||||
onShowEnhanceSql,
|
||||
onShowEnhanceJava,
|
||||
registerCustomButtonModal,
|
||||
registerEnhanceJsModal,
|
||||
registerEnhanceSqlModal,
|
||||
registerEnhanceJavaModal,
|
||||
registerAuthManagerDrawer,
|
||||
registerAuthSetterModal,
|
||||
registerCgformModal,
|
||||
registerDbToOnlineModal,
|
||||
registerAddressModal,
|
||||
} = useCgformList({
|
||||
pageType,
|
||||
designScope: 'online-cgform-list',
|
||||
columns: [
|
||||
{ title: '视图表名', dataIndex: 'tableName' },
|
||||
{ title: '视图表描述', dataIndex: 'tableTxt' },
|
||||
{ title: '原表版本', dataIndex: 'copyVersion' },
|
||||
{ title: '视图版本', dataIndex: 'tableVersion' },
|
||||
],
|
||||
formSchemas: [{ label: '表名', field: 'tableName', component: 'JInput' }],
|
||||
});
|
||||
const { prefixCls, tableContext } = pageContext;
|
||||
const [registerTable, { reload }, { rowSelection }] = tableContext;
|
||||
watch(router.currentRoute, () => reload());
|
||||
return {
|
||||
prefixCls,
|
||||
reload,
|
||||
rowSelection,
|
||||
getTableAction,
|
||||
getDropDownAction,
|
||||
onShowCustomButton,
|
||||
onShowEnhanceJs,
|
||||
onShowEnhanceSql,
|
||||
onShowEnhanceJava,
|
||||
registerCustomButtonModal,
|
||||
registerEnhanceJsModal,
|
||||
registerEnhanceSqlModal,
|
||||
registerEnhanceJavaModal,
|
||||
registerAuthManagerDrawer,
|
||||
registerAuthSetterModal,
|
||||
registerTable,
|
||||
registerCgformModal,
|
||||
registerDbToOnlineModal,
|
||||
registerAddressModal,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,168 @@
|
||||
<!-- online查询条件中的下拉搜索 -->
|
||||
<template>
|
||||
<a-select
|
||||
:value="selected"
|
||||
:placeholder="placeholder"
|
||||
show-search
|
||||
:default-active-first-option="false"
|
||||
:show-arrow="true"
|
||||
:filter-option="false"
|
||||
:not-found-content="null"
|
||||
@search="handleSearch"
|
||||
@change="handleChange"
|
||||
@popupScroll="handlePopupScroll"
|
||||
allowClear
|
||||
>
|
||||
<a-select-option v-for="d in selectOptions" :key="d.value">
|
||||
{{ d.text }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
const { createMessage: $message } = useMessage();
|
||||
import { watch, ref } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'JOnlineSearchSelect',
|
||||
props: {
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
// online CgReport item id
|
||||
fieldId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ['update:value'],
|
||||
setup(props, { emit }) {
|
||||
let selected = ref('');
|
||||
let selectOptions = ref([]);
|
||||
let isHasData = true;
|
||||
let scrollLoading = false;
|
||||
let searchKeyword = '';
|
||||
const pageNo = ref(1);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newVal) => {
|
||||
if (!newVal) {
|
||||
selected.value = undefined;
|
||||
} else {
|
||||
selected.value = newVal;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.fieldId,
|
||||
() => {
|
||||
resetOptions();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
/**
|
||||
* 2024-07-17
|
||||
* liaozhiyang
|
||||
* 【TV360X-1813】online报表查询支持滚动加载
|
||||
* */
|
||||
const handleSearch = useDebounceFn((keyword) => {
|
||||
searchKeyword = keyword;
|
||||
pageNo.value = 1;
|
||||
isHasData = true;
|
||||
searchByKeyword(keyword);
|
||||
}, 800);
|
||||
|
||||
/**
|
||||
* 2024-07-17
|
||||
* liaozhiyang
|
||||
* 【TV360X-1813】online报表查询支持滚动加载
|
||||
* */
|
||||
async function searchByKeyword(keyword = '') {
|
||||
let params = {
|
||||
keyword: keyword,
|
||||
fieldId: props.fieldId,
|
||||
pageSize: 10,
|
||||
pageNo: pageNo.value,
|
||||
};
|
||||
let url = `/online/cgreport/api/getReportDictList`;
|
||||
await defHttp
|
||||
.get({ url: url, params }, { isTransformResponse: false })
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
if (res.result && res.result.length > 0) {
|
||||
if (pageNo.value == 1) {
|
||||
selectOptions.value = [...res.result];
|
||||
} else {
|
||||
selectOptions.value.push(...res.result);
|
||||
}
|
||||
pageNo.value++;
|
||||
} else {
|
||||
if (pageNo.value == 1) {
|
||||
selectOptions.value = [];
|
||||
}
|
||||
isHasData = false;
|
||||
}
|
||||
} else {
|
||||
$message.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
pageNo.value != 1 && pageNo.value--;
|
||||
});
|
||||
}
|
||||
|
||||
function handleChange(value) {
|
||||
emit('update:value', value);
|
||||
//点击clear按钮,重置下拉项
|
||||
if (!value || value == '') {
|
||||
resetOptions();
|
||||
}
|
||||
}
|
||||
function resetOptions() {
|
||||
selectOptions.value = [];
|
||||
// update-begin--author:liaozhiyang---date:20240717---for:【TV360X-1813】online报表查询支持滚动加载
|
||||
pageNo.value = 1;
|
||||
isHasData = true;
|
||||
searchKeyword = '';
|
||||
// update-end--author:liaozhiyang---date:20240717---for:【TV360X-1813】online报表查询支持滚动加载
|
||||
searchByKeyword();
|
||||
}
|
||||
/**
|
||||
* 2024-07-17
|
||||
* liaozhiyang
|
||||
* 【TV360X-1813】online报表查询支持滚动加载
|
||||
* */
|
||||
const handlePopupScroll = async (e) => {
|
||||
const { target } = e;
|
||||
const { scrollTop, scrollHeight, clientHeight } = target;
|
||||
if (!scrollLoading && isHasData && scrollTop + clientHeight >= scrollHeight - 10) {
|
||||
scrollLoading = true;
|
||||
searchByKeyword(searchKeyword).finally(() => {
|
||||
scrollLoading = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
return {
|
||||
selectOptions,
|
||||
handleSearch,
|
||||
handleChange,
|
||||
selected,
|
||||
handlePopupScroll,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,353 @@
|
||||
<template>
|
||||
<div :id="tableName + '_form'">
|
||||
|
||||
<!-- 积木报表的打印按钮,只有配置了 reportUrl 才显示 -->
|
||||
<div v-if="!!formData.id && !!onlineExtConfigJson.reportPrintShow" style="text-align: right;position: absolute;top: 15px;right: 20px;z-index: 999">
|
||||
<PrinterOutlined title="打印" @click="onOpenReportPrint" style="font-size: 16px"/>
|
||||
</div>
|
||||
|
||||
<detail-form :schemas="detailFormSchemas" :data="formData" :span="formSpan"></detail-form>
|
||||
|
||||
<!-- 子表 -->
|
||||
<a-tabs v-if="themeTemplate !== ERP && hasSubTable && showSub" @change="onTabChange">
|
||||
<a-tab-pane v-for="(sub, index) in subTabInfo" :tab="sub.describe" :key="index + ''" :forceRender="true">
|
||||
<div :style="{ 'overflow-y': 'auto', 'overflow-x': 'hidden', 'max-height': subFormHeight + 'px' }" v-if="sub.relationType == 1">
|
||||
<!-- 子表-一对一 -->
|
||||
<online-sub-form-detail :key="subReloadKey" :table="sub.key" :form-template="formTemplate" :main-id="getSubTableForeignKeyValue(sub.foreignKey)" :properties="sub.properties"> </online-sub-form-detail>
|
||||
</div>
|
||||
<div v-else>
|
||||
<!-- 子表-一对多 -->
|
||||
<JVxeTable
|
||||
v-if="showStatus[sub.key]"
|
||||
:ref="refMap[sub.key]"
|
||||
keep-source
|
||||
:row-number="rowNumber"
|
||||
row-selection
|
||||
:height="subTableHeight"
|
||||
:disabled="true"
|
||||
:columns="sub.columns"
|
||||
:dataSource="subDataSource[sub.key]"
|
||||
:authPre="getSubTableAuthPre(sub.key)"
|
||||
/>
|
||||
<a-spin v-else :spinning="true"/>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<Loading :loading="loading" :absolute="false" />
|
||||
<slot name="bottom"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Loading } from '/@/components/Loading';
|
||||
import { getToken } from '/@/utils/auth';
|
||||
import { goJmReportViewPage } from '/@/utils';
|
||||
import { PrinterOutlined } from '@ant-design/icons-vue';
|
||||
import DetailForm from '../../extend/form/DetailForm.vue';
|
||||
import OnlineSubFormDetail from './OnlineSubFormDetail.vue';
|
||||
import { getDetailFormSchemas } from '../../hooks/auto/useAutoForm';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { ERP } from "../../util/constant";
|
||||
import { useAppInject } from '/@/hooks/web/useAppInject';
|
||||
import { useOnlineFormDetailContext } from '../../hooks/auto/useAutoFormDetail';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
export default {
|
||||
name: 'OnlineFormDetail',
|
||||
components: {
|
||||
DetailForm,
|
||||
Loading,
|
||||
PrinterOutlined,
|
||||
OnlineSubFormDetail,
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
formTemplate: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isTree: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
pidField: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
submitTip: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showSub:{
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
themeTemplate: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
},
|
||||
emits: ['success', 'rendered'],
|
||||
setup(props, { emit }) {
|
||||
console.log('onlineForm-setup》》');
|
||||
const { createMessage: $message } = useMessage();
|
||||
const { getIsMobile } = useAppInject();
|
||||
const tableName = ref('');
|
||||
const single = ref(true);
|
||||
// 加载状态
|
||||
const loading = ref(false);
|
||||
const tableType = ref(1);
|
||||
|
||||
const formData = ref<any>({});
|
||||
// update-begin-author:liaozhiyang---date:20240313---for:【QQYUN-9034】online弹窗一对一子表移动端内容高度设置不合理
|
||||
const subFormHeight = ref(getIsMobile.value ? 'auto' : 300);
|
||||
// update-end-author:liaozhiyang---date:20240313---for:【QQYUN-9034】online弹窗一对一子表移动端内容高度设置不合理
|
||||
const subReloadKey = ref(0);
|
||||
// 子表表格高度
|
||||
// 【VUEN-803】一对多子表固定340高度,修复自定义列组件被遮挡的问题
|
||||
const subTableHeight = ref(340);
|
||||
|
||||
const rowNumber = ref(getIsMobile.value ? false : true);
|
||||
|
||||
let detailData = {};
|
||||
// 字段展示状态
|
||||
const fieldDisplayStatus = reactive<any>({});
|
||||
|
||||
/**
|
||||
* online表单扩展配置
|
||||
*/
|
||||
const onlineExtConfigJson = reactive({
|
||||
reportPrintShow: 0,
|
||||
reportPrintUrl: '',
|
||||
joinQuery: 0,
|
||||
modelFullscreen: 0,
|
||||
modalMinWidth: '',
|
||||
});
|
||||
|
||||
const { detailFormSchemas, hasSubTable, subTabInfo, refMap, showStatus, subDataSource, createFormSchemas, formSpan } = getDetailFormSchemas(props);
|
||||
|
||||
/**
|
||||
* 处理扩展配置
|
||||
*/
|
||||
function handleExtConfigJson(jsonStr) {
|
||||
let extConfigJson = { reportPrintShow: 0, reportPrintUrl: '', joinQuery: 0, modelFullscreen: 1, modalMinWidth: '' };
|
||||
if (jsonStr) {
|
||||
extConfigJson = JSON.parse(jsonStr);
|
||||
}
|
||||
Object.keys(extConfigJson).map((k) => {
|
||||
onlineExtConfigJson[k] = extConfigJson[k];
|
||||
});
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240425---for:【issues/6139】online详情支持js增强loaded事件及设置值、获取值、隐藏功能
|
||||
const { onlineFormDetailContext, resetContext } = useOnlineFormDetailContext();
|
||||
let { EnhanceJS, initCgEnhanceJs } = useEnhance(onlineFormDetailContext, false);
|
||||
// update-end--author:liaozhiyang---date:20240425---for:【issues/6139】online详情支持js增强loaded事件及设置值、获取值、隐藏功能
|
||||
|
||||
// 渲染表单
|
||||
async function createRootProperties(data) {
|
||||
tableType.value = data.head.tableType;
|
||||
tableName.value = data.head.tableName;
|
||||
single.value = data.head.tableType == 1;
|
||||
handleExtConfigJson(data.head.extConfigJson);
|
||||
createFormSchemas(data.schema.properties);
|
||||
// update-begin--author:liaozhiyang---date:20240425---for:【issues/6139】online详情支持js增强loaded事件及设置值、获取值、隐藏功能
|
||||
EnhanceJS = initCgEnhanceJs(data.enhanceJs);
|
||||
// update-end--author:liaozhiyang---date:20240425---for:【issues/6139】online详情支持js增强loaded事件及设置值、获取值、隐藏功能
|
||||
emit('rendered', onlineExtConfigJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* status: 是否是修改页面
|
||||
* record: 列表页面的行数据
|
||||
* param: 树形列表添加子节点 传入的父级节点id
|
||||
* */
|
||||
async function show(_status, record) {
|
||||
console.log('进入表单详情》》form', record);
|
||||
// -update-begin--author:liaozhiyang---date:20251209---for:【QQYUN-13970】一对一子表编辑之后查看详情不会更新
|
||||
subReloadKey.value++;
|
||||
// -update-end--author:liaozhiyang---date:20251209---for:【QQYUN-13970】一对一子表编辑之后查看详情不会更新
|
||||
await edit(record);
|
||||
changeShowStatus(true);
|
||||
}
|
||||
|
||||
function getFormData(dataId) {
|
||||
let url = `/online/cgform/api/detail/${props.id}/${dataId}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
defHttp
|
||||
.get({ url }, { isTransformResponse: false })
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
resolve(res.result);
|
||||
} else {
|
||||
reject();
|
||||
$message.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//update-begin-author:taoyan date:2023-2-13 for: QQYUN-4226【vue3】online 一对多子表 详情界面,序号错位了 点一下子表表格就正常了
|
||||
function changeShowStatus(flag){
|
||||
Object.keys(showStatus).map(k=>{
|
||||
showStatus[k] = flag;
|
||||
})
|
||||
}
|
||||
|
||||
function onTabChange(){
|
||||
changeShowStatus(false);
|
||||
setTimeout(()=>{
|
||||
changeShowStatus(true);
|
||||
}, 300);
|
||||
}
|
||||
//update-end-author:taoyan date:2023-2-13 for: QQYUN-4226【vue3】online 一对多子表 详情界面,序号错位了 点一下子表表格就正常了
|
||||
|
||||
async function edit(record) {
|
||||
let temp: any = await getFormData(record.id);
|
||||
// update-begin--author:liaozhiyang---date:20240425---for:【issues/6139】online详情支持js增强loaded事件及设置值、获取值、隐藏功能
|
||||
detailData = temp;
|
||||
// 每次打开有js增强设置隐藏的,都要先置成初始化。
|
||||
detailFormSchemas.value.filter((item) => item.hidden).forEach((item) => (item.hidden = false));
|
||||
Object.keys(fieldDisplayStatus).forEach(function (key) {
|
||||
delete fieldDisplayStatus[key];
|
||||
});
|
||||
handleEnhanceJS({ buttonCode: 'loaded' });
|
||||
// 表单赋值
|
||||
formData.value = { ...detailData };
|
||||
editSubVxeTableData(detailData);
|
||||
// update-end--author:liaozhiyang---date:20240425---for:【issues/6139】online详情支持js增强loaded事件及设置值、获取值、隐藏功能
|
||||
}
|
||||
|
||||
function editSubVxeTableData(record) {
|
||||
if (!record) {
|
||||
// 新增页面需要清空子表数据
|
||||
record = {};
|
||||
}
|
||||
let keys = Object.keys(subDataSource.value);
|
||||
if (keys && keys.length > 0) {
|
||||
let obj = {};
|
||||
for (let key of keys) {
|
||||
obj[key] = record[key] || [];
|
||||
}
|
||||
subDataSource.value = obj;
|
||||
}
|
||||
}
|
||||
|
||||
function getSubTableAuthPre(table) {
|
||||
return 'online_' + table + ':';
|
||||
}
|
||||
|
||||
//跳转至积木报表页面
|
||||
function onOpenReportPrint() {
|
||||
let url = onlineExtConfigJson.reportPrintUrl;
|
||||
let temp: any = formData.value;
|
||||
if (temp) {
|
||||
let id = temp.id;
|
||||
let token = getToken();
|
||||
goJmReportViewPage(url, id, token);
|
||||
}
|
||||
}
|
||||
|
||||
function getSubTableForeignKeyValue(key) {
|
||||
let temp = formData.value;
|
||||
console.log('getValueIgnoreCase(temp, key)', temp, key, getValueIgnoreCase(temp, key));
|
||||
return getValueIgnoreCase(temp, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* VUEN-1056 30、生成的一对多,编辑的时候,子表数据挂不上
|
||||
*/
|
||||
function getValueIgnoreCase(data, key) {
|
||||
if (data) {
|
||||
let temp = data[key];
|
||||
if (!temp && temp !== 0) {
|
||||
temp = data[key.toLowerCase()];
|
||||
if (!temp && temp !== 0) {
|
||||
temp = data[key.toUpperCase()];
|
||||
}
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240425---for:【issues/6139】online详情支持js增强loaded事件及设置值、获取值、隐藏功能
|
||||
function handleEnhanceJS({ buttonCode }) {
|
||||
if (EnhanceJS && EnhanceJS[buttonCode]) {
|
||||
EnhanceJS[buttonCode].call(onlineFormDetailContext, onlineFormDetailContext);
|
||||
}
|
||||
}
|
||||
watch(fieldDisplayStatus, (newValue) => {
|
||||
Object.entries(newValue).forEach(([key, value]) => {
|
||||
if (value == false) {
|
||||
const findItem = detailFormSchemas.value.find((item) => item.field === key);
|
||||
if (findItem) {
|
||||
findItem.hidden = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
const context = {
|
||||
setFieldsValue: (values) => {
|
||||
Object.entries(values).forEach(([key, value]) => {
|
||||
detailData[key] = value;
|
||||
});
|
||||
},
|
||||
getFieldsValue: () => {
|
||||
return { ...detailData };
|
||||
},
|
||||
sh: fieldDisplayStatus,
|
||||
isUpdate: ref(false),
|
||||
isDetail: ref(true),
|
||||
};
|
||||
resetContext(context);
|
||||
// update-end--author:liaozhiyang---date:20240425---for:【issues/6139】online详情支持js增强loaded事件及设置值、获取值、隐藏功能
|
||||
|
||||
return {
|
||||
detailFormSchemas,
|
||||
formData,
|
||||
formSpan,
|
||||
|
||||
//主表
|
||||
tableName,
|
||||
loading,
|
||||
|
||||
//子表
|
||||
hasSubTable,
|
||||
subTabInfo,
|
||||
subFormHeight,
|
||||
subTableHeight,
|
||||
refMap,
|
||||
onTabChange,
|
||||
subReloadKey,
|
||||
|
||||
//一对多子表
|
||||
subDataSource,
|
||||
getSubTableAuthPre,
|
||||
|
||||
//父组件调用
|
||||
show,
|
||||
createRootProperties,
|
||||
|
||||
// 扩展配置
|
||||
onOpenReportPrint,
|
||||
onlineExtConfigJson,
|
||||
getSubTableForeignKeyValue,
|
||||
showStatus,
|
||||
ERP,
|
||||
rowNumber,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,911 @@
|
||||
<template>
|
||||
<div :id="tableName + '_form'" class="onlinePopFormWrap" :class="[`formTemplate_${formTemplate}`]">
|
||||
<BasicForm ref="onlineFormRef" @register="registerForm" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { computed, ref, unref, nextTick, toRaw, reactive } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { SUBMIT_FLOW_KEY, VALIDATE_FAILED } from '../../types/onlineRender';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { pick } from 'lodash-es';
|
||||
import { useFormItems, getRefPromise, useOnlineFormContext } from '../../hooks/auto/useAutoForm';
|
||||
import { Loading } from '/@/components/Loading';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import OnlineSubForm from './OnlineSubForm.vue';
|
||||
import { loadFormFieldsDefVal } from '../../util/FieldDefVal';
|
||||
import { getToken } from '/@/utils/auth';
|
||||
import { goJmReportViewPage } from '/@/utils'
|
||||
import { PrinterOutlined, DiffOutlined, FormOutlined } from '@ant-design/icons-vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { Method } from 'axios';
|
||||
import { isObject } from '/@/utils/is';
|
||||
|
||||
const urlObject = {
|
||||
optPre: '/online/cgform/api/form/',
|
||||
urlButtonAction: '/online/cgform/api/doButton',
|
||||
};
|
||||
export default {
|
||||
name: 'OnlinePopForm',
|
||||
components: {
|
||||
BasicForm,
|
||||
Loading,
|
||||
OnlineSubForm,
|
||||
PrinterOutlined,
|
||||
DiffOutlined,
|
||||
FormOutlined
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
formTemplate: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isTree: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
pidField: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
submitTip:{
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
modalClass:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
//是否发送请求-即表单的保存/编辑请求,false则只将表单数据抛出去
|
||||
request: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否是vxeTable上方按钮点击打开的表单数据
|
||||
isVxeTableData: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['success', 'rendered', 'dataChange'],
|
||||
setup(props, { emit }) {
|
||||
console.log('onlineForm-setup》》');
|
||||
const { createMessage: $message } = useMessage();
|
||||
|
||||
const [registerVxeFormModal, { openModal:openVxeFormModal }] = useModal();
|
||||
const vxeTableId = ref('');
|
||||
// 表单ref
|
||||
const onlineFormRef = ref(null);
|
||||
const single = ref(true);
|
||||
// 加载状态
|
||||
const loading = ref(false);
|
||||
const tableType = ref(1);
|
||||
// 表单提交且提交流程
|
||||
const submitFlowFlag = ref(false);
|
||||
const isUpdate = ref(false);
|
||||
|
||||
/**
|
||||
* online表单扩展配置
|
||||
*/
|
||||
const onlineExtConfigJson = reactive({
|
||||
reportPrintShow: 0,
|
||||
reportPrintUrl: '',
|
||||
joinQuery: 0,
|
||||
modelFullscreen: 0,
|
||||
modalMinWidth: '',
|
||||
});
|
||||
|
||||
const { onlineFormContext, resetContext } = useOnlineFormContext();
|
||||
const {
|
||||
formSchemas,
|
||||
defaultValueFields,
|
||||
changeDataIfArray2String,
|
||||
tableName,
|
||||
dbData,
|
||||
checkOnlyFieldValue,
|
||||
hasSubTable,
|
||||
subTabInfo,
|
||||
refMap,
|
||||
subDataSource,
|
||||
baseColProps,
|
||||
createFormSchemas,
|
||||
fieldDisplayStatus,
|
||||
labelCol,
|
||||
wrapperCol,
|
||||
labelWidth
|
||||
} = useFormItems(props, onlineFormRef);
|
||||
let { EnhanceJS, initCgEnhanceJs } = useEnhance(onlineFormContext, false);
|
||||
|
||||
//表单配置
|
||||
const [registerForm, { setProps, validate, resetFields, setFieldsValue, updateSchema, getFieldsValue, scrollToField }] = useForm({
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: baseColProps,
|
||||
// update-begin--author:liaozhiyang---date:20240329---for:【QQYUN-7872】online表单label较长优化
|
||||
labelWidth,
|
||||
// update-end--author:liaozhiyang---date:20240329---for:【QQYUN-7872】online表单label较长优化
|
||||
// update-begin--author:liaozhiyang---date:20240105---for:【QQYUN-7499】多列风格富文本、markdown增加独占一行功能
|
||||
labelCol,
|
||||
wrapperCol
|
||||
// update-end--author:liaozhiyang---date:20240105---for:【QQYUN-7499】多列风格富文本、markdown增加独占一行功能
|
||||
});
|
||||
|
||||
// 表单禁用
|
||||
const onlineFormDisabled = ref(false);
|
||||
function handleFormDisabled() {
|
||||
let flag = props.disabled;
|
||||
onlineFormDisabled.value = flag;
|
||||
setProps({ disabled: flag });
|
||||
}
|
||||
|
||||
/**
|
||||
* status: 是否是修改页面
|
||||
* record: 列表页面的行数据
|
||||
* param: 树形列表添加子节点 传入的父级节点id
|
||||
* */
|
||||
async function show(status, record, param) {
|
||||
console.log('onlinepopform新增编辑进入表单》》form', record);
|
||||
await resetFields();
|
||||
dbData.value = '';
|
||||
let flag = unref(status);
|
||||
isUpdate.value = flag;
|
||||
if (flag) {
|
||||
// 编辑页面
|
||||
await edit(record);
|
||||
}
|
||||
await nextTick(() => {
|
||||
if (!flag && param) {
|
||||
//如果是新增页面 且 param传入有值 需要设置表单
|
||||
setFieldsValue(param);
|
||||
}
|
||||
handleDefaultValue();
|
||||
// 所有信息加载完毕 触发loaded事件
|
||||
handleCgButtonClick('js', 'loaded');
|
||||
//处理表单的禁用效果
|
||||
handleFormDisabled();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 当前表单默认值逻辑-进入新增页面触发
|
||||
*/
|
||||
function handleDefaultValue() {
|
||||
if (unref(isUpdate) === false) {
|
||||
let fieldProperties = toRaw(defaultValueFields[tableName.value]);
|
||||
loadFormFieldsDefVal(fieldProperties, (values) => {
|
||||
setFieldsValue(values);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function edit(record) {
|
||||
// 查询数据库
|
||||
let formData:any = await getFormData(record.id);
|
||||
if(!formData || Object.keys(formData).length==0){
|
||||
//没有查询出数据
|
||||
formData = {...toRaw(record)}
|
||||
}
|
||||
dbData.value = Object.assign({}, formData);
|
||||
//表单赋值
|
||||
let arr = realFormFieldNames.value;
|
||||
let values = pick(formData, ...arr);
|
||||
// 如果是vxetable上方按钮打开的表单,那么表单值以record为主,而不是数据库查询的数据,否则第一次修改后第二次打开表单,表单值和数据库一致但是和当前页面不一致
|
||||
if(props.isVxeTableData === true){
|
||||
values = Object.assign({},values, record)
|
||||
}
|
||||
await setFieldsValue(values);
|
||||
// editSubVxeTableData(formData);
|
||||
}
|
||||
|
||||
function editSubVxeTableData(record) {
|
||||
if (!record) {
|
||||
// 新增页面需要清空子表数据
|
||||
record = {};
|
||||
}
|
||||
let keys = Object.keys(subDataSource.value);
|
||||
if (keys && keys.length > 0) {
|
||||
let obj = {};
|
||||
for (let key of keys) {
|
||||
obj[key] = record[key] || [];
|
||||
}
|
||||
subDataSource.value = obj;
|
||||
}
|
||||
}
|
||||
|
||||
let realFormFieldNames = computed(() => {
|
||||
let arr = formSchemas.value;
|
||||
let names = [];
|
||||
for (let a of arr) {
|
||||
names.push(a.field);
|
||||
}
|
||||
return names;
|
||||
});
|
||||
|
||||
function getFormData(dataId) {
|
||||
let url = `${urlObject.optPre}${props.id}/${dataId}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
defHttp
|
||||
.get({ url }, { isTransformResponse: false })
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
resolve(res.result);
|
||||
} else {
|
||||
reject();
|
||||
$message.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染表单
|
||||
async function createRootProperties(data) {
|
||||
tableType.value = data.head.tableType;
|
||||
tableName.value = data.head.tableName;
|
||||
single.value = data.head.tableType == 1;
|
||||
handleExtConfigJson(data.head.extConfigJson);
|
||||
|
||||
createFormSchemas(data.schema.properties, data.schema.required, checkOnlyFieldValue, onlineExtConfigJson);
|
||||
EnhanceJS = initCgEnhanceJs(data.enhanceJs);
|
||||
emit('rendered', onlineExtConfigJson);
|
||||
|
||||
//监听表单改变事件
|
||||
let formRefObject:any = await getRefPromise(onlineFormRef);
|
||||
formRefObject.$formValueChange = (field, value, changeFormData) => {
|
||||
onValuesChange(field, value);
|
||||
if(changeFormData){
|
||||
//如果存在其他表单控件的数据,直接设置该值
|
||||
setFieldsValue(changeFormData)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理扩展配置
|
||||
*/
|
||||
function handleExtConfigJson(jsonStr) {
|
||||
let extConfigJson = { reportPrintShow: 0, reportPrintUrl: '', joinQuery: 0, modelFullscreen: 1, modalMinWidth: '', formLabelLength: null };
|
||||
if (jsonStr) {
|
||||
extConfigJson = JSON.parse(jsonStr);
|
||||
}
|
||||
Object.keys(extConfigJson).map((k) => {
|
||||
onlineExtConfigJson[k] = extConfigJson[k];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function handleSubmit() {
|
||||
if (single.value === true) {
|
||||
handleSingleSubmit();
|
||||
} else {
|
||||
handleOne2ManySubmit();
|
||||
}
|
||||
}
|
||||
|
||||
function handleOne2ManySubmit() {
|
||||
validateAll().then((formData) => {
|
||||
handleApplyRequest(formData);
|
||||
});
|
||||
}
|
||||
|
||||
// 触发所有表单验证
|
||||
function validateAll() {
|
||||
let temp = {};
|
||||
return new Promise((resolve, reject) => {
|
||||
// 验证主表表单
|
||||
validate().then(
|
||||
(values) => resolve(values),
|
||||
({ errorFields }) => {
|
||||
reject({
|
||||
code: VALIDATE_FAILED,
|
||||
key: tableName.value,
|
||||
// 滚动到未通过校验的字段上
|
||||
scrollToField: () => errorFields[0] && scrollToField(errorFields[0].name, { behavior: 'smooth', block: 'center' }),
|
||||
});
|
||||
}
|
||||
);
|
||||
})
|
||||
.then((result) => {
|
||||
Object.assign(temp, changeDataIfArray2String(result));
|
||||
return validateSubTableFields();
|
||||
})
|
||||
.then((allTableData) => {
|
||||
Object.assign(temp, allTableData);
|
||||
return Promise.resolve(temp);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e === VALIDATE_FAILED || e?.code === VALIDATE_FAILED) {
|
||||
$message.warning('校验未通过');
|
||||
if (e.key) {
|
||||
changeTab(e.key);
|
||||
if (e.scrollToField) {
|
||||
setTimeout(() => e.scrollToField(), 150)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error(e);
|
||||
}
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换tab到出现校验错误的页面
|
||||
* */
|
||||
function changeTab(key) {
|
||||
let arr = subTabInfo.value;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (key == arr[i].key) {
|
||||
subActiveKey.value = i + '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证子表
|
||||
function validateSubTableFields() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let subData = {};
|
||||
try {
|
||||
let arr = subTabInfo.value;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let key = arr[i].key;
|
||||
let instance = refMap[key].value;
|
||||
// 兼容写法:如果取到的是一个数组类型,则取第一个元素
|
||||
if (instance instanceof Array) {
|
||||
instance = instance[0];
|
||||
}
|
||||
if (arr[i].relationType == 1) {
|
||||
try {
|
||||
let subFormData = await instance.getAll();
|
||||
subData[key] = [];
|
||||
subData[key].push(subFormData);
|
||||
} catch (e) {
|
||||
return reject({code: VALIDATE_FAILED, key, ...e});
|
||||
}
|
||||
} else {
|
||||
let errMap = await instance.fullValidateTable();
|
||||
if (errMap) {
|
||||
return reject({code: VALIDATE_FAILED, key});
|
||||
}
|
||||
subData[key] = instance.getTableData();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
resolve(subData);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单提交-单表
|
||||
*/
|
||||
async function handleSingleSubmit() {
|
||||
try {
|
||||
let values = await validate();
|
||||
values = Object.assign({}, dbData.value, values);
|
||||
values = changeDataIfArray2String(values);
|
||||
loading.value = true;
|
||||
handleApplyRequest(values);
|
||||
} catch (error) {
|
||||
// update-begin--author:liaozhiyang---date:20240524---for:【TV360X-420】关联记录校验不通过的项在可视区外时点击保存没任何效果
|
||||
if (isObject(error)) {
|
||||
const errorFields = error.errorFields;
|
||||
if (errorFields?.length && errorFields[0].errors) {
|
||||
$message.warning(errorFields[0].errors[0]);
|
||||
scrollToField(errorFields[0].name, { behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
}
|
||||
console.log(error);
|
||||
// update-end--author:liaozhiyang---date:20240524---for:【TV360X-420】关联记录校验不通过的项在可视区外时点击保存没任何效果
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
//提交数据前 先走一下自定义的JS校验
|
||||
function handleApplyRequest(formData) {
|
||||
customBeforeSubmit(context, formData)
|
||||
.then(() => {
|
||||
doApplyRequest(formData);
|
||||
})
|
||||
.catch((msg) => {
|
||||
$message.warning(msg);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function triggleChangeValues(values, id, target) {
|
||||
if (id && target) {
|
||||
if (target.setValues) {
|
||||
//一对一子表
|
||||
target.setValues(values);
|
||||
} else {
|
||||
//一对多子表
|
||||
target.setValues([
|
||||
{
|
||||
rowKey: id,
|
||||
values: values,
|
||||
},
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
//主表
|
||||
setFieldsValue(values);
|
||||
}
|
||||
}
|
||||
function triggleChangeValue(field, value) {
|
||||
let obj = {};
|
||||
obj[field] = value;
|
||||
setFieldsValue(obj);
|
||||
}
|
||||
|
||||
// 一对多子表Tab的Key,用于校验未通过时自动跳转
|
||||
const subActiveKey = ref('0');
|
||||
const subFormHeight = ref(300);
|
||||
// 子表表格高度
|
||||
// 【VUEN-803】一对多子表固定340高度,修复自定义列组件被遮挡的问题
|
||||
const subTableHeight = ref(340);
|
||||
|
||||
function getSubTableForeignKeyValue(key) {
|
||||
if (isUpdate.value === true) {
|
||||
let formData = dbData.value;
|
||||
return getValueIgnoreCase(formData, key);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* VUEN-1056 30、生成的一对多,编辑的时候,子表数据挂不上
|
||||
*/
|
||||
function getValueIgnoreCase(data, key) {
|
||||
if (data) {
|
||||
let temp = data[key];
|
||||
if (!temp && temp !== 0) {
|
||||
temp = data[key.toLowerCase()];
|
||||
if (!temp && temp !== 0) {
|
||||
temp = data[key.toUpperCase()];
|
||||
}
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
//处理一对一子表的表单改变事件
|
||||
function handleSubFormChange(valueObj, tableKey) {
|
||||
if (EnhanceJS && EnhanceJS[tableKey + '_onlChange']) {
|
||||
let tableChangeObj = EnhanceJS[tableKey + '_onlChange']();
|
||||
let columnKey = Object.keys(valueObj)[0];
|
||||
if (tableChangeObj[columnKey]) {
|
||||
let subRef = refMap[tableKey].value;
|
||||
if (subRef instanceof Array) {
|
||||
subRef = subRef[0];
|
||||
}
|
||||
let formEvent = subRef.getFormEvent();
|
||||
let event = {
|
||||
column: { key: columnKey },
|
||||
value: valueObj[columnKey],
|
||||
...formEvent,
|
||||
};
|
||||
tableChangeObj[columnKey].call(onlineFormContext, onlineFormContext, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//处理一对多子表的改变事件
|
||||
function handleValueChange(event, tableKey) {
|
||||
if (EnhanceJS && EnhanceJS[tableKey + '_onlChange']) {
|
||||
let tableChangeObj = EnhanceJS[tableKey + '_onlChange'](onlineFormContext);
|
||||
if (tableChangeObj[event.column.key]) {
|
||||
tableChangeObj[event.column.key].call(onlineFormContext, onlineFormContext, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 当行编辑新增完成后触发的事件
|
||||
function handleAdded(sub, event) {
|
||||
console.log('handleAdded', sub, event)
|
||||
//update-begin-author:taoyan date:2022-6-26 for: 控制台警告 这里直接调用函数 不触发事件了
|
||||
// event.target.emit('executeFillRule', event);
|
||||
//update-end-author:taoyan date:2022-6-26 for: 控制台警告 这里直接调用函数 不触发事件了
|
||||
}
|
||||
|
||||
function getSubTableAuthPre(table) {
|
||||
return 'online_' + table + ':';
|
||||
}
|
||||
|
||||
//监听表单改变事件
|
||||
async function onValuesChange(columnKey, value) {
|
||||
//console.log('columnKey-value', `${columnKey}-${value}`)
|
||||
// 将老数据和新数据比较 如果不同 往外抛出改变事件
|
||||
let oldFormData = dbData.value;
|
||||
if(oldFormData[columnKey]!=value){
|
||||
emit('dataChange', columnKey);
|
||||
}
|
||||
|
||||
if (!EnhanceJS || !EnhanceJS['onlChange']) {
|
||||
return false;
|
||||
}
|
||||
if (!columnKey) {
|
||||
return false;
|
||||
}
|
||||
//let tableChangeObj = EnhanceJS["onlChange"].call(onlineFormContext);
|
||||
let tableChangeObj = EnhanceJS['onlChange']();
|
||||
if (tableChangeObj[columnKey]) {
|
||||
let formData = await getFieldsValue();
|
||||
let event = {
|
||||
row: formData,
|
||||
column: { key: columnKey },
|
||||
value: value,
|
||||
};
|
||||
tableChangeObj[columnKey].call(onlineFormContext, onlineFormContext, event);
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义按钮 增强触发事件
|
||||
function handleCgButtonClick(optType, buttonCode) {
|
||||
if ('js' == optType) {
|
||||
if (EnhanceJS && EnhanceJS[buttonCode]) {
|
||||
EnhanceJS[buttonCode].call(onlineFormContext, onlineFormContext);
|
||||
}
|
||||
} else if ('action' == optType) {
|
||||
let formData = dbData.value;
|
||||
let params = {
|
||||
formId: props.id,
|
||||
buttonCode: buttonCode,
|
||||
dataId: formData.id,
|
||||
uiFormData: Object.assign({}, formData),
|
||||
};
|
||||
//console.log("自定义按钮请求后台参数:",params)
|
||||
defHttp
|
||||
.post(
|
||||
{
|
||||
url: `${urlObject.urlButtonAction}`,
|
||||
params,
|
||||
},
|
||||
{ isTransformResponse: false }
|
||||
)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
$message.success('处理完成!');
|
||||
} else {
|
||||
$message.warning('处理失败!');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------增强-------------------------
|
||||
|
||||
/**
|
||||
* 清除子表数据
|
||||
* @param tbname
|
||||
*/
|
||||
function clearSubRows(tbname) {
|
||||
let instance = refMap[tbname].value;
|
||||
let rows = [...instance.getNewDataWithId(), ...subDataSource.value[tbname]];
|
||||
if (!rows || rows.length == 0) {
|
||||
return false;
|
||||
}
|
||||
let ids = [];
|
||||
for (let i of rows) {
|
||||
ids.push(i.id);
|
||||
}
|
||||
instance.removeRowsById(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加子表数据
|
||||
* @param tbname
|
||||
* @param rows 可以是数组也可以是对象
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function addSubRows(tbname, rows) {
|
||||
if (!rows) {
|
||||
return false;
|
||||
}
|
||||
let instance = refMap[tbname].value;
|
||||
if (typeof rows == 'object') {
|
||||
instance.addRows(rows, true);
|
||||
} else {
|
||||
$message.error('添加子表数据,参数不识别!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 先删除后添加
|
||||
* @param tbname
|
||||
* @param rows
|
||||
*/
|
||||
function clearThenAddRows(tbname, rows) {
|
||||
clearSubRows(tbname);
|
||||
addSubRows(tbname, rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改下拉框的下拉选项
|
||||
* @param field
|
||||
* @param options
|
||||
*/
|
||||
function changeOptions(field, options) {
|
||||
if (!options && options.length <= 0) {
|
||||
options = [];
|
||||
}
|
||||
options.map((item) => {
|
||||
if (!item.hasOwnProperty('label')) {
|
||||
item['label'] = item.text;
|
||||
}
|
||||
});
|
||||
updateSchema({
|
||||
field,
|
||||
componentProps: {
|
||||
options,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单提交前事件
|
||||
* @param that
|
||||
* @param formData
|
||||
* @returns {Promise<void>|*}
|
||||
*/
|
||||
function customBeforeSubmit(that, formData) {
|
||||
if (EnhanceJS && EnhanceJS['beforeSubmit']) {
|
||||
return EnhanceJS['beforeSubmit'](that, formData);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自定义弹框 字段的显示隐藏
|
||||
* @param show
|
||||
* @param hide
|
||||
*/
|
||||
function handleCustomFormSh(show, hide) {
|
||||
let plain = toRaw(fieldDisplayStatus);
|
||||
if (show && show.length > 0) {
|
||||
Object.keys(plain).map((k) => {
|
||||
if (!k.endsWith('_load') && show.indexOf(k) < 0) {
|
||||
fieldDisplayStatus[k] = false;
|
||||
}
|
||||
});
|
||||
} else if (hide && hide.length > 0) {
|
||||
Object.keys(plain).map((k) => {
|
||||
if (hide.indexOf(k) >= 0) {
|
||||
fieldDisplayStatus[k] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCustomFormEdit(record) {
|
||||
console.log('自定义弹窗打开online表单》》form', record);
|
||||
await resetFields();
|
||||
dbData.value = '';
|
||||
isUpdate.value = true;
|
||||
// 编辑数据
|
||||
await edit(record);
|
||||
await nextTick(() => {
|
||||
// 所有信息加载完毕 触发loaded事件
|
||||
handleCgButtonClick('js', 'loaded');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* VUEN-1036
|
||||
* 获取子表的实例对象 可以直接调用子表的方法
|
||||
*/
|
||||
function getSubTableInstance(tableName) {
|
||||
let instance = refMap[tableName].value;
|
||||
if (instance instanceof Array) {
|
||||
instance = instance[0];
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
//跳转至积木报表页面
|
||||
function onOpenReportPrint(){
|
||||
let url = onlineExtConfigJson.reportPrintUrl;
|
||||
let id = dbData.value.id;
|
||||
let token = getToken();
|
||||
goJmReportViewPage(url, id, token)
|
||||
}
|
||||
|
||||
//----
|
||||
function openSubFormModalForAdd(sub){
|
||||
console.log(sub)
|
||||
vxeTableId.value = sub.id;
|
||||
openVxeFormModal(true, )
|
||||
}
|
||||
|
||||
function openSubFormModalForEdit(sub){
|
||||
console.log(sub)
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据
|
||||
* @param formData
|
||||
*/
|
||||
function doApplyRequest(formData) {
|
||||
// 数组没有元素直接置空
|
||||
Object.keys(formData).map((key) => {
|
||||
if (Array.isArray(formData[key])) {
|
||||
if (formData[key].length == 0) {
|
||||
formData[key] = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('提交pop表单数据》》》form:', formData);
|
||||
if(props.request == false){
|
||||
emit('success', formData);
|
||||
}else{
|
||||
let url = `${urlObject.optPre}${props.id}?tabletype=${tableType.value}`;
|
||||
|
||||
console.log('提交pop表单url》》》url:', url);
|
||||
// 如果需要提交流程 需要额外设置一个参数
|
||||
if (submitFlowFlag.value === true) {
|
||||
formData[SUBMIT_FLOW_KEY] = 1;
|
||||
}
|
||||
let method:Method = isUpdate.value === true ? 'put' : 'post';
|
||||
defHttp.request({ url, method, params: formData }, { isTransformResponse: false })
|
||||
.then((res) => {
|
||||
//console.log('表单提交完成', res)
|
||||
if (res.success) {
|
||||
if (res.result) {
|
||||
//formData[SUBMIT_FLOW_ID] = res.result;
|
||||
if(!formData.id){
|
||||
formData['id'] = res.result;
|
||||
}
|
||||
}
|
||||
//刷新列表
|
||||
emit('success', formData);
|
||||
dbData.value = formData;
|
||||
isUpdate.value = true;
|
||||
$message.success('操作成功!')
|
||||
// 工单申请提交的表单也会走这个逻辑,保存成功不需要提示信息
|
||||
} else {
|
||||
$message.warning(res.message);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 数据恢复到 dbdata
|
||||
*/
|
||||
async function recoverFormData(){
|
||||
let record = dbData.value;
|
||||
let arr = realFormFieldNames.value;
|
||||
let values = pick(record, ...arr);
|
||||
if(record){
|
||||
await setFieldsValue(values);
|
||||
}else{
|
||||
let temp:any = {}
|
||||
for(let key of arr){
|
||||
temp[key] = ''
|
||||
}
|
||||
await setFieldsValue(temp);
|
||||
}
|
||||
}
|
||||
|
||||
let context = {
|
||||
tableName,
|
||||
loading,
|
||||
subActiveKey,
|
||||
onlineFormRef,
|
||||
getFieldsValue,
|
||||
setFieldsValue,
|
||||
submitFlowFlag,
|
||||
subFormHeight,
|
||||
subTableHeight,
|
||||
refMap,
|
||||
triggleChangeValues,
|
||||
triggleChangeValue,
|
||||
sh: fieldDisplayStatus,
|
||||
clearSubRows,
|
||||
addSubRows,
|
||||
clearThenAddRows,
|
||||
changeOptions,
|
||||
isUpdate,
|
||||
getSubTableInstance,
|
||||
};
|
||||
resetContext(context);
|
||||
|
||||
return {
|
||||
//主表
|
||||
tableName,
|
||||
onlineFormRef,
|
||||
registerForm,
|
||||
loading,
|
||||
|
||||
//子表
|
||||
subActiveKey,
|
||||
hasSubTable,
|
||||
subTabInfo,
|
||||
refMap,
|
||||
|
||||
//一对一子表
|
||||
subFormHeight,
|
||||
getSubTableForeignKeyValue,
|
||||
isUpdate,
|
||||
handleSubFormChange,
|
||||
|
||||
//一对多子表
|
||||
subTableHeight,
|
||||
onlineFormDisabled,
|
||||
subDataSource,
|
||||
getSubTableAuthPre,
|
||||
handleAdded,
|
||||
handleValueChange,
|
||||
openSubFormModalForAdd,
|
||||
openSubFormModalForEdit,
|
||||
registerVxeFormModal,
|
||||
vxeTableId,
|
||||
|
||||
//父组件调用
|
||||
show,
|
||||
createRootProperties,
|
||||
handleSubmit,
|
||||
sh: fieldDisplayStatus,
|
||||
handleCgButtonClick,
|
||||
handleCustomFormSh,
|
||||
handleCustomFormEdit,
|
||||
//跳转
|
||||
dbData,
|
||||
onOpenReportPrint,
|
||||
onlineExtConfigJson,
|
||||
recoverFormData
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.onlinePopFormWrap {
|
||||
// update-begin--author:liaozhiyang---date:20240429---for:【QQYUN-7632】 label栅格改成labelwidth固宽
|
||||
padding: 20px 1.5% 0 1.5%;
|
||||
// update-begin--author:liaozhiyang---date:20240506---for:【QQYUN-9229】间隔调整
|
||||
&.formTemplate_1 {
|
||||
> form {
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
}
|
||||
}
|
||||
&.formTemplate_2 {
|
||||
> form {
|
||||
padding-left: 1%;
|
||||
padding-right: 1%;
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240506---for:【QQYUN-9229】间隔调整
|
||||
:deep(.ant-form) {
|
||||
> .ant-row {
|
||||
> .ant-col {
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240429---for:【QQYUN-7632】 label栅格改成labelwidth固宽
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" :width="popModalFixedWidth" :dialogStyle="{top: '70px'}" :bodyStyle="popBodyStyle" :title="modalTitle" wrapClassName="jeecg-online-pop-list-modal">
|
||||
<template #footer>
|
||||
<div style="display: inline-block;width: calc(100% - 140px);text-align: left;">
|
||||
<a-button v-if="addAuth" style="border-radius: 50px" type="primary" @click="handleAdd"><PlusOutlined/>新增记录</a-button>
|
||||
</div>
|
||||
<a-button key="back" @click="handleCancel">关闭</a-button>
|
||||
<a-button :disabled="submitDisabled" key="submit" type="primary" @click="handleSubmit" :loading="submitLoading">确定</a-button>
|
||||
</template>
|
||||
|
||||
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
|
||||
<!-- update-begin-author:taoyan date:2023-7-11 for: issues/4992 online表单开发 字段控件类型是关联记录 新增的时候选择列表可以添加查询么 -->
|
||||
<template #tableTitle>
|
||||
<a-input-search v-model:value="searchText" @search="onSearch" placeholder="请输入关键词,按回车搜索" style="width: 240px" />
|
||||
</template>
|
||||
<!-- update-end-author:taoyan date:2023-7-11 for: issues/4992 online表单开发 字段控件类型是关联记录 新增的时候选择列表可以添加查询么 -->
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)">
|
||||
</TableAction>
|
||||
</template>
|
||||
|
||||
<template #fileSlot="{ text }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
|
||||
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download" size="small" @click="downloadRowFile(text)"> 下载 </a-button>
|
||||
</template>
|
||||
|
||||
<template #imgSlot="{ text }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
|
||||
<img v-else :src="getImgView(text)" alt="图片不存在" class="online-cell-image" @click="viewOnlineCellImage(text)" />
|
||||
</template>
|
||||
|
||||
<template #htmlSlot="{ text }">
|
||||
<div v-html="text"></div>
|
||||
</template>
|
||||
|
||||
<template #pcaSlot="{ text, column }">
|
||||
<div :title="getPcaText(text, column)">{{ getPcaText(text, column) }}</div>
|
||||
</template>
|
||||
|
||||
<template #dateSlot="{ text, column }">
|
||||
<span>{{ getFormatDate(text, column) }}</span>
|
||||
</template>
|
||||
|
||||
</BasicTable>
|
||||
</BasicModal>
|
||||
|
||||
<!-- 弹窗到另外一张表单用-可编辑表单 -->
|
||||
<online-pop-modal :id="id" @register="registerPopModal" @success="handleDataSave" topTip></online-pop-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch, ref, toRaw, computed } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useTableColumns } from '../../hooks/auto/useTableColumns';
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import OnlinePopModal from './OnlinePopModal.vue';
|
||||
import { useFixedHeightModal } from '../../hooks/auto/useAutoModal';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OnlinePopListModal',
|
||||
props: {
|
||||
/**可以是表名 可以是ID*/
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
multi:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
addAuth:{
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BasicModal,
|
||||
BasicTable,
|
||||
TableAction,
|
||||
PlusOutlined,
|
||||
OnlinePopModal
|
||||
},
|
||||
emits: ['success', 'register'],
|
||||
setup(props, { emit }) {
|
||||
const { createMessage: $message } = useMessage();
|
||||
// 弹窗高度控制
|
||||
const { popModalFixedWidth, resetBodyStyle, popBodyStyle } = useFixedHeightModal();
|
||||
const searchText = ref('');
|
||||
const modalWidth = ref(800);
|
||||
//useModalInner
|
||||
const [registerModal, {closeModal}] = useModalInner((data) => {
|
||||
searchText.value = '';
|
||||
// update-begin--author:liaozhiyang---date:20240517---for:【TV360X-43】修复关联记录可以添加重复数据
|
||||
selectedRowKeys.value = data.selectedRowKeys;
|
||||
selectedRows.value = data.selectedRows;
|
||||
// update-end--author:liaozhiyang---date:20240517---for:【TV360X-43】修复关联记录可以添加重复数据
|
||||
setPagination({current:1})
|
||||
reload();
|
||||
resetBodyStyle();
|
||||
});
|
||||
|
||||
// 用于 online表单中 弹出别的表单
|
||||
const [registerPopModal, { openModal: openPopModal }] = useModal();
|
||||
|
||||
function handleCancel() {
|
||||
closeModal();
|
||||
}
|
||||
const submitDisabled = computed(()=>{
|
||||
const arr = selectedRowKeys.value;
|
||||
if(arr && arr.length>0){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
const submitLoading = ref(false);
|
||||
function handleSubmit(){
|
||||
submitLoading.value = true;
|
||||
let arr = toRaw(selectedRows.value);
|
||||
if(arr && arr.length>0){
|
||||
emit('success', arr)
|
||||
closeModal();
|
||||
}
|
||||
setTimeout(()=>{
|
||||
submitLoading.value = false
|
||||
}, 200);
|
||||
}
|
||||
|
||||
//---------------------列表------------------------
|
||||
function queryTableData(params){
|
||||
const url = '/online/cgform/api/getData/'+props.id;
|
||||
return defHttp.get({ url, params });
|
||||
}
|
||||
|
||||
function list(params){
|
||||
params['column'] = 'id';
|
||||
return new Promise(async (resolve, _reject) => {
|
||||
const aa = await queryTableData(params)
|
||||
resolve(aa);
|
||||
})
|
||||
}
|
||||
|
||||
const onlineTableContext = {
|
||||
isPopList: true,
|
||||
reloadTable(){
|
||||
console.log('reloadTable')
|
||||
},
|
||||
isTree(){
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const extConfigJson = ref<any>({});
|
||||
|
||||
// 处理 BasicTable 的配置
|
||||
const {
|
||||
columns,
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
handleColumnResult,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
} = useTableColumns(onlineTableContext, extConfigJson);
|
||||
|
||||
|
||||
/**
|
||||
* 查询table列信息 及其他配置
|
||||
*/
|
||||
function getColumnList() {
|
||||
const url = '/online/cgform/api/getColumns/'+props.id;
|
||||
return new Promise((resolve, reject) => {
|
||||
defHttp.get({url}, { isTransformResponse: false }).then((res) => {
|
||||
if (res.success) {
|
||||
resolve(res.result);
|
||||
} else {
|
||||
$message.warning(res.message);
|
||||
reject();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const modalTitle = ref('')
|
||||
watch(()=>props.id, async ()=>{
|
||||
let columnResult:any = await getColumnList();
|
||||
handleColumnResult(columnResult);
|
||||
modalTitle.value = columnResult.description;
|
||||
}, {immediate: true})
|
||||
|
||||
const { tableContext } = useListPage({
|
||||
designScope: 'process-design',
|
||||
pagination: true,
|
||||
tableProps: {
|
||||
title: '',
|
||||
api: list,
|
||||
clickToRowSelect: true,
|
||||
columns: columns,
|
||||
showTableSetting: false,
|
||||
immediate:false,
|
||||
//showIndexColumn: true,
|
||||
canResize: false,
|
||||
showActionColumn: false,
|
||||
actionColumn: {
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
useSearchForm: false,
|
||||
beforeFetch: (params) => {
|
||||
return addQueryParams(params);
|
||||
},
|
||||
},
|
||||
});
|
||||
const [registerTable, { reload, setPagination }, { rowSelection, selectedRowKeys, selectedRows }] = tableContext;
|
||||
watch(()=>props.multi, (val)=>{
|
||||
if(val==true){
|
||||
rowSelection.type = 'checkbox'
|
||||
}else{
|
||||
rowSelection.type = 'radio'
|
||||
}
|
||||
}, {immediate: true});
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleUpdate.bind(null, record),
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
function handleUpdate(record){
|
||||
console.log('handleUpdate', record)
|
||||
}
|
||||
|
||||
function onSearch(){
|
||||
reload();
|
||||
}
|
||||
const eqConditonTypes = ['int', 'double', 'Date', 'Datetime', 'BigDecimal']
|
||||
function addQueryParams(params){
|
||||
let text = searchText.value;
|
||||
if(!text){
|
||||
params['superQueryMatchType'] = 'or';
|
||||
params['superQueryParams'] = ''
|
||||
return params;
|
||||
}
|
||||
let arr = columns.value;
|
||||
let conditions:any[] = []
|
||||
if(arr && arr.length>0){
|
||||
for(let item of arr){
|
||||
if(item.dbType){
|
||||
if(item.dbType == 'string'){
|
||||
conditions.push({field: item.dataIndex,type:item.dbType.toLowerCase(), rule: 'like', val: text})
|
||||
}else if(item.dbType == 'Date'){
|
||||
if(text.length=='2020-10-10'.length){
|
||||
conditions.push({field: item.dataIndex,type:item.dbType.toLowerCase(), rule: 'eq', val: text})
|
||||
}
|
||||
}else if(item.dbType == 'Datetime'){
|
||||
if(text.length=='2020-10-10 10:10:10'.length){
|
||||
conditions.push({field: item.dataIndex,type:item.dbType.toLowerCase(), rule: 'eq', val: text})
|
||||
}
|
||||
}else if(eqConditonTypes.indexOf(item.dbType)){
|
||||
conditions.push({field: item.dataIndex, type:item.dbType.toLowerCase(), rule: 'eq', val: text})
|
||||
}else{
|
||||
//text blob不做处理
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
params['superQueryMatchType'] = 'or';
|
||||
params['superQueryParams'] = encodeURI(JSON.stringify(conditions));
|
||||
return params;
|
||||
}
|
||||
|
||||
function handleAdd(){
|
||||
openPopModal(true, {})
|
||||
}
|
||||
|
||||
// modal数据新增完成 直接关闭list,将新增的数据带回表单
|
||||
function handleDataSave(data){
|
||||
console.log('handleDateSave' ,data)
|
||||
// update-begin--author:liaozhiyang---date:20250429---for:【issues/8163】关联记录新增丢失
|
||||
let arr = [data, ...selectedRows.value];
|
||||
// update-end--author:liaozhiyang---date:20250429---for:【issues/8163】关联记录新增丢失
|
||||
emit('success', arr);
|
||||
closeModal();
|
||||
//reload();
|
||||
}
|
||||
|
||||
return {
|
||||
registerModal,
|
||||
modalWidth,
|
||||
handleCancel,
|
||||
submitDisabled,
|
||||
submitLoading,
|
||||
handleSubmit,
|
||||
|
||||
registerTable,
|
||||
getTableAction,
|
||||
searchText,
|
||||
onSearch,
|
||||
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
rowSelection,
|
||||
modalTitle,
|
||||
|
||||
registerPopModal,
|
||||
handleAdd,
|
||||
reload,
|
||||
|
||||
popModalFixedWidth,
|
||||
popBodyStyle,
|
||||
handleDataSave
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.online-cell-image {
|
||||
max-height: 30px;
|
||||
max-width: 50px;
|
||||
object-fit: contain;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<BasicModal :width="popModalFixedWidth" :dialogStyle="{top: '70px'}" :bodyStyle="popBodyStyle" v-bind="$attrs" :footer="modalFooter" cancelText="关闭" @register="registerModal" wrapClassName="jeecg-online-pop-modal" @ok="handleSubmit">
|
||||
<template #title>
|
||||
{{title}}
|
||||
<j-modal-tip v-if="showTopTip" :visible="topTipVisible" @save="handleSaveData" @cancel="handleRecover"></j-modal-tip>
|
||||
</template>
|
||||
|
||||
<online-pop-form
|
||||
ref="onlineFormCompRef"
|
||||
:id="id"
|
||||
:disabled="disableSubmit"
|
||||
:form-template="formTemplate"
|
||||
:isTree="isTreeForm"
|
||||
:pidField="pidFieldName"
|
||||
:request="request"
|
||||
:isVxeTableData="isVxeTableData"
|
||||
@rendered="renderSuccess"
|
||||
@success="handleSuccess"
|
||||
@data-change="handleDataChange"
|
||||
modal-class="jeecg-online-pop-modal"
|
||||
>
|
||||
</online-pop-form>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch, watchEffect, ref, computed, h } from 'vue';
|
||||
import { BasicModal } from '/@/components/Modal';
|
||||
import OnlinePopForm from './OnlinePopForm.vue';
|
||||
import { useAutoModal } from '../../hooks/auto/useAutoModal';
|
||||
import JModalTip from '../../extend/linkTable/JModalTip.vue'
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OnlinePopModal',
|
||||
props: {
|
||||
/**可以是表名 可以是ID*/
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
/*展示字段名*/
|
||||
showFields:{
|
||||
type: Array,
|
||||
default: ()=>[],
|
||||
},
|
||||
/*隐藏字段名*/
|
||||
hideFields:{
|
||||
type: Array,
|
||||
default: ()=>[],
|
||||
},
|
||||
topTip:{
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
request:{
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
saveClose:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否是vxeTable上方按钮点击打开的表单数据
|
||||
isVxeTableData:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
formTableType:{
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// -update-begin--author:liaozhiyang---date:20240613---for:【TV360X-1000】流程一对多走流程的接口
|
||||
// 有taskId即是流程
|
||||
taskId: {
|
||||
type: String,
|
||||
},
|
||||
tableName: {
|
||||
type: String,
|
||||
},
|
||||
// -update-end--author:liaozhiyang---date:20240613---for:【TV360X-1000】流程一对多走流程的接口
|
||||
},
|
||||
components: {
|
||||
BasicModal,
|
||||
OnlinePopForm,
|
||||
JModalTip,
|
||||
Button
|
||||
},
|
||||
emits: ['success', 'register', 'formConfig'],
|
||||
setup(props, { emit }) {
|
||||
console.log('进入表单弹框》》》》modal');
|
||||
|
||||
const {
|
||||
title,
|
||||
registerModal,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
disableSubmit,
|
||||
handleSubmit,
|
||||
submitLoading,
|
||||
handleCancel,
|
||||
handleFormConfig,
|
||||
onlineFormCompRef,
|
||||
formTemplate,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
renderSuccess,
|
||||
formRendered,
|
||||
handleSuccess,
|
||||
topTipVisible,
|
||||
successThenClose,
|
||||
isUpdate,
|
||||
popBodyStyle,
|
||||
popModalFixedWidth,
|
||||
getFormStatus
|
||||
} = useAutoModal(false, { emit });
|
||||
|
||||
// 监听id变化 表单重新渲染
|
||||
watch(() => props.id, renderFormItems, { immediate: true });
|
||||
async function renderFormItems() {
|
||||
formRendered.value = false;
|
||||
if (!props.id) {
|
||||
return;
|
||||
}
|
||||
console.log('重新渲染表单》》》》modal');
|
||||
|
||||
//update-begin-author:taoyan date:2023-4-10 for: issues/4655 online在线表单(一对多),对子表记录进行新增或编辑时,无法获取到表单信息 #4655
|
||||
let params = {}
|
||||
if(props.formTableType){
|
||||
params['tabletype'] = props.formTableType
|
||||
}
|
||||
// -update-begin--author:liaozhiyang---date:20240613---for:【TV360X-1000】流程一对多走流程的接口
|
||||
if (props.taskId) {
|
||||
await handleFormConfig(props.id, params,null,props.taskId, props.tableName );
|
||||
} else {
|
||||
await handleFormConfig(props.id, params);
|
||||
}
|
||||
// -update-end--author:liaozhiyang---date:20240613---for:【TV360X-1000】流程一对多走流程的接口
|
||||
//update-end-author:taoyan date:2023-4-10 for: issues/4655 online在线表单(一对多),对子表记录进行新增或编辑时,无法获取到表单信息 #4655
|
||||
|
||||
}
|
||||
|
||||
// 上方保存按钮触发
|
||||
function handleSaveData() {
|
||||
//如果props的saveClose没有设置为true则弹窗不会关闭
|
||||
if(props.saveClose === false){
|
||||
successThenClose.value = false;
|
||||
}
|
||||
handleSubmit();
|
||||
}
|
||||
|
||||
// 上方取消按钮触发
|
||||
function handleRecover(){
|
||||
topTipVisible.value = false;
|
||||
onlineFormCompRef.value.recoverFormData()
|
||||
}
|
||||
|
||||
// 表单数据改变触发modal事件
|
||||
function handleDataChange(){
|
||||
topTipVisible.value = true;
|
||||
}
|
||||
|
||||
// 只有编辑页面才需要显示顶部保存按钮
|
||||
const showTopTip = computed(()=>{
|
||||
// update-begin--author:liaozhiyang---date:20250318---for:【issues/7930】表格列表中支持关联记录配置是否只读
|
||||
if (disableSubmit.value) {
|
||||
return false;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20250318---for:【issues/7930】表格列表中支持关联记录配置是否只读
|
||||
if(!isUpdate.value){
|
||||
return false;
|
||||
}
|
||||
return props.topTip
|
||||
});
|
||||
|
||||
// 编辑页面没有底部按钮
|
||||
const modalFooter = computed(()=>{
|
||||
if(isUpdate.value==true){
|
||||
return null;
|
||||
}else{
|
||||
let flag = submitLoading.value;
|
||||
const defaultFooter:any[] = [
|
||||
h(Button, { type: 'primary', loading: flag, onClick: handleSubmit },()=>'确定'),
|
||||
h(Button, { onClick:handleCancel },()=>'关闭')
|
||||
];
|
||||
return defaultFooter
|
||||
}
|
||||
});
|
||||
|
||||
const that = {
|
||||
title,
|
||||
topTipVisible,
|
||||
handleSaveData,
|
||||
handleRecover,
|
||||
onlineFormCompRef,
|
||||
renderSuccess,
|
||||
registerModal,
|
||||
handleSubmit,
|
||||
handleSuccess,
|
||||
handleCancel,
|
||||
formTemplate,
|
||||
disableSubmit,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
submitLoading,
|
||||
handleDataChange,
|
||||
isUpdate,
|
||||
showTopTip,
|
||||
modalFooter,
|
||||
popBodyStyle,
|
||||
popModalFixedWidth,
|
||||
getFormStatus
|
||||
};
|
||||
|
||||
return that;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,863 @@
|
||||
<template>
|
||||
<div class="jeecg-basic-table-form-container online-query-form p-0" v-if="formSchemas && formSchemas.length > 0">
|
||||
<BasicForm ref="onlineQueryFormRef" @register="registerForm">
|
||||
<!-- 范围查询:日期 -->
|
||||
<template #groupDate="{ model, field, schema }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换 -->
|
||||
<!-- <a-date-picker
|
||||
:showTime="false"
|
||||
valueFormat="YYYY-MM-DD"
|
||||
placeholder="开始日期"
|
||||
v-model:value="model[field + '_begin']"
|
||||
style="width: calc(50% - 15px);min-width: 100px;"
|
||||
v-bind="schema.componentProps"
|
||||
></a-date-picker>
|
||||
<span class="group-query-string">~</span>
|
||||
<a-form-item-rest>
|
||||
<a-date-picker
|
||||
:showTime="false"
|
||||
valueFormat="YYYY-MM-DD"
|
||||
placeholder="结束日期"
|
||||
v-model:value="model[field + '_end']"
|
||||
style="width: calc(50% - 15px);min-width: 100px;"
|
||||
v-bind="schema.componentProps"
|
||||
></a-date-picker>
|
||||
</a-form-item-rest> -->
|
||||
<a-range-picker :style="{ width: '100%' }" v-model:value="model[field]" v-bind="schema.componentProps" :placeholder="getGroupDatePlaceholder(schema.componentProps)" valueFormat="YYYY-MM-DD"/>
|
||||
<!-- update-end--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换 -->
|
||||
</template>
|
||||
|
||||
<!-- 范围查询:时间 -->
|
||||
<template #groupDatetime="{ model, field }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换 -->
|
||||
<!-- <a-date-picker
|
||||
:showTime="true"
|
||||
valueFormat="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="开始时间"
|
||||
v-model:value="model[field + '_begin']"
|
||||
style="min-width: 100px;width: calc(50% - 15px);"
|
||||
></a-date-picker>
|
||||
<span class="group-query-string">~</span>
|
||||
<a-form-item-rest>
|
||||
<a-date-picker
|
||||
:showTime="true"
|
||||
valueFormat="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="结束时间"
|
||||
v-model:value="model[field + '_end']"
|
||||
style="min-width: 100px;width: calc(50% - 15px);"
|
||||
></a-date-picker>
|
||||
</a-form-item-rest> -->
|
||||
<a-range-picker :style="{ width: '100%' }" v-model:value="model[field]" :show-time="true" valueFormat="YYYY-MM-DD HH:mm:ss"/>
|
||||
<!-- update-end--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换 -->
|
||||
</template>
|
||||
|
||||
<!-- update-begin--author:liaozhiyang---date:20240517---for:【QQYUN-9348】增加online查询区域时间范围查询功能 -->
|
||||
<!-- 范围查询:时间 -->
|
||||
<template #groupTime="{ model, field }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换 -->
|
||||
<!-- <a-time-picker
|
||||
placeholder="开始时间"
|
||||
value-format="HH:mm:ss"
|
||||
v-model:value="model[field + '_begin']"
|
||||
style="min-width: 100px;width: calc(50% - 15px);"
|
||||
></a-time-picker>
|
||||
<span class="group-query-string">~</span>
|
||||
<a-form-item-rest>
|
||||
<a-time-picker
|
||||
placeholder="结束时间"
|
||||
value-format="HH:mm:ss"
|
||||
v-model:value="model[field + '_end']"
|
||||
style="min-width: 100px;width: calc(50% - 15px);"
|
||||
></a-time-picker>
|
||||
</a-form-item-rest> -->
|
||||
<a-time-range-picker :style="{ width: '100%' }" v-model:value="model[field]" value-format="HH:mm:ss" />
|
||||
<!-- update-end--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换 -->
|
||||
</template>
|
||||
<!-- update-end--author:liaozhiyang---date:20240517---for:【QQYUN-9348】增加online查询区域时间范围查询功能 -->
|
||||
|
||||
<!-- 范围查询:数值 -->
|
||||
<template #groupNumber="{ model, field, schema }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换 -->
|
||||
<!-- <a-input-number placeholder="开始值" v-model:value="model[field + '_begin']" style="width: calc(50% - 15px)"></a-input-number>
|
||||
<span class="group-query-string">~</span>
|
||||
<a-form-item-rest>
|
||||
<a-input-number placeholder="结束值" v-model:value="model[field + '_end']" style="width: calc(50% - 15px)"></a-input-number>
|
||||
</a-form-item-rest> -->
|
||||
<JRangeNumber v-model:value="model[field]" v-bind="schema.componentProps" />
|
||||
<!-- update-end--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换 -->
|
||||
</template>
|
||||
|
||||
<!-- 查询/重置按钮-->
|
||||
<template #formFooter>
|
||||
<a-col :md="6" :sm="8">
|
||||
<span style="float: left; overflow: hidden; margin-left: 10px" class="table-page-search-submitButtons">
|
||||
<a-button
|
||||
v-if="queryBtnCfg.enabled"
|
||||
type="primary"
|
||||
:preIcon="queryBtnCfg.buttonIcon"
|
||||
@click="doSearch"
|
||||
>
|
||||
<span>{{ queryBtnCfg.buttonName }}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="resetBtnCfg.enabled"
|
||||
type="primary"
|
||||
:preIcon="resetBtnCfg.buttonIcon"
|
||||
style="margin-left: 8px"
|
||||
@click="resetSearch"
|
||||
>
|
||||
<span>{{ resetBtnCfg.buttonName }}</span>
|
||||
</a-button>
|
||||
<a v-if="toggleButtonShow" @click="toggleSearchStatus = !toggleSearchStatus" style="margin-left: 8px">
|
||||
{{ toggleSearchStatus ? '收起' : '展开' }}
|
||||
<a-icon :type="toggleSearchStatus ? 'up' : 'down'" />
|
||||
</a>
|
||||
</span>
|
||||
</a-col>
|
||||
</template>
|
||||
</BasicForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { watch, ref, reactive, toRaw, isProxy } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import FormSchemaFactory from './factory/FormSchemaFactory';
|
||||
import IFormSchema from './factory/IFormSchema';
|
||||
import { handleLinkDown, getFieldIndex, getRefPromise, LINK_DOWN } from '../../hooks/auto/useAutoForm';
|
||||
import { ONL_QUERY_LABEL_COL, ONL_QUERY_WRAPPER_COL, FORM_VIEW_TO_QUERY_VIEW } from '../../types/onlineRender';
|
||||
import { loadOneFieldDefVal } from '../../util/FieldDefVal';
|
||||
import { useExtendComponent } from '../../hooks/auto/useExtendComponent';
|
||||
import { LABELLENGTH } from '../../util/constant';
|
||||
import dayjs from 'dayjs';
|
||||
import JRangeNumber from '/@/components/Form/src/jeecg/components/JRangeNumber.vue'
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
|
||||
export default {
|
||||
name: 'OnlineQueryForm',
|
||||
components: {
|
||||
BasicForm,
|
||||
JRangeNumber,
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
queryBtnCfg: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
enabled: true,
|
||||
buttonName: '查询',
|
||||
buttonIcon: 'ant-design:search',
|
||||
}
|
||||
}
|
||||
},
|
||||
resetBtnCfg: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
enabled: true,
|
||||
buttonName: '重置',
|
||||
buttonIcon: 'ant-design:reload',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
emits: ['search', 'loaded'],
|
||||
setup(props, { emit }) {
|
||||
// 获取查询条件请求地址
|
||||
const LOAD_URL = '/online/cgform/api/getQueryInfoVue3/';
|
||||
// 表单ref
|
||||
const onlineQueryFormRef = ref(null);
|
||||
// 表单渲染用到的配置
|
||||
const formSchemas = ref([]);
|
||||
// 表单栅格 VUEN-2493【优化】online默认查询条件太宽了,参考online报表
|
||||
const baseColProps = ref({ xs:24, sm: 24, md: 12, lg:6, xl:6 });
|
||||
// 切换字段显示隐藏按钮是否显示
|
||||
const toggleButtonShow = ref(false);
|
||||
// 是否显示所有查询字段
|
||||
const toggleSearchStatus = ref(false);
|
||||
// 查询条件
|
||||
const queryParams = ref({});
|
||||
// 需要隐藏的字段
|
||||
const hideList = ref([]);
|
||||
const { createMessage: $message } = useMessage();
|
||||
const { linkTableCard2Select } = useExtendComponent();
|
||||
const formLabelWidth = ref(80);
|
||||
/**
|
||||
* 默认值分三种,param > cache > config
|
||||
* 1.表单配置-config
|
||||
* 2.路由缓存-cache
|
||||
* 3.地址栏参数-param
|
||||
* 当表单id发生改变,config改变,列表页传入cache,param;监听status change然后重置表单的值
|
||||
*/
|
||||
const defaultValues = reactive({
|
||||
config: {},
|
||||
cache: {},
|
||||
param: {},
|
||||
status: false,
|
||||
});
|
||||
|
||||
const debouncedCustomSetFieldsValue = useDebounceFn(customSetFieldsValue, 500);
|
||||
|
||||
/**
|
||||
* 监听cacheFormValues
|
||||
*/
|
||||
watch(
|
||||
() => defaultValues.status,
|
||||
async (val) => {
|
||||
console.log('-------------defaultValues发生改变,需要重置表单---------------');
|
||||
const { config, cache, param } = toRaw(defaultValues);
|
||||
let rawValues = Object.assign({}, config, cache, param);
|
||||
//update-begin---author:wangshuai---date:2025-10-11---for:【issues/8790】online 表单重大 bug,影响配置了查询 的所有表单---
|
||||
await debouncedCustomSetFieldsValue(rawValues);
|
||||
//update-end---author:wangshuai---date:2025-10-11---for:【issues/8790】online 表单重大 bug,影响配置了查询 的所有表单---
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
);
|
||||
|
||||
/**
|
||||
* 设置默认值
|
||||
* @param values
|
||||
*/
|
||||
async function initDefaultValues(cache, param) {
|
||||
defaultValues.cache = { ...cache };
|
||||
defaultValues.param = { ...param };
|
||||
defaultValues.status = !defaultValues.status;
|
||||
}
|
||||
|
||||
const clearObj = (obj) => {
|
||||
Object.keys(obj).map((key) => {
|
||||
delete obj[key];
|
||||
});
|
||||
};
|
||||
|
||||
// 监听
|
||||
watch(
|
||||
() => props.id,
|
||||
(val) => {
|
||||
if (val) {
|
||||
resetForm();
|
||||
} else {
|
||||
formSchemas.value = [];
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取表单配置
|
||||
*/
|
||||
async function initSchemas(formProperties) {
|
||||
let arr = [];
|
||||
let configValue = {};
|
||||
let keys = Object.keys(formProperties);
|
||||
let setLabelLength = -1;
|
||||
for (let key of keys) {
|
||||
const item = formProperties[key];
|
||||
|
||||
//update-begin-author:taoyan date:2023-7-19 for:QQYUN-5783 配置的数据字典参数,问题是在查询的部门选择组件那儿,表单定义不能更改传给后端org_code。看后端代码,给组件value传的也是id,这儿需要调整。
|
||||
if(key === 'sys_org_code'){
|
||||
if(!item.fieldExtendJson){
|
||||
item.fieldExtendJson = '{"store":"orgCode"}'
|
||||
}
|
||||
}
|
||||
//update-end-author:taoyan date:2023-7-19 for:QQYUN-5783 配置的数据字典参数,问题是在查询的部门选择组件那儿,表单定义不能更改传给后端org_code。看后端代码,给组件value传的也是id,这儿需要调整。
|
||||
|
||||
let view = item.view;
|
||||
// update-begin--author:liaozhiyang---date:20240611---for:【TV360X-461】字段类型是string,控件是text,则默认模糊查询
|
||||
item.originView = item.view;
|
||||
// update-end--author:liaozhiyang---date:20240611---for:【TV360X-461】字段类型是string,控件是text,则默认模糊查询
|
||||
if (FORM_VIEW_TO_QUERY_VIEW[view]) {
|
||||
item.view = FORM_VIEW_TO_QUERY_VIEW[view];
|
||||
}
|
||||
await loadOneFieldDefVal(key, item, configValue);
|
||||
if (item.mode == 'group' && ('date' == view || 'datetime' == view || 'number' == view || 'time' == view )) {
|
||||
// 范围查询-日期,时间,数值
|
||||
let temp = FormSchemaFactory.createSlotFormSchema(key, item);
|
||||
arr.push(temp);
|
||||
} else {
|
||||
if (item.view === LINK_DOWN) {
|
||||
let array = handleLinkDown(item, key);
|
||||
for (let linkDownItem of array) {
|
||||
let temp = FormSchemaFactory.createFormSchema(linkDownItem.key, linkDownItem);
|
||||
let tempIndex = getFieldIndex(arr, linkDownItem.key);
|
||||
if (tempIndex == -1) {
|
||||
arr.push(temp);
|
||||
} else {
|
||||
arr[tempIndex] = temp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let tempIndex = getFieldIndex(arr, key);
|
||||
if (tempIndex == -1) {
|
||||
let temp = FormSchemaFactory.createFormSchema(key, item);
|
||||
arr.push(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20231205---for:【QQYUN-7140】online label默认显示6个
|
||||
let fieldExtendJson = item.fieldExtendJson;
|
||||
if (fieldExtendJson) {
|
||||
fieldExtendJson = JSON.parse(fieldExtendJson);
|
||||
if (fieldExtendJson.labelLength) {
|
||||
console.log(key, fieldExtendJson.labelLength);
|
||||
if (setLabelLength > -1) {
|
||||
// 取配置中设置的最大值,所有label的长度应一致,否则多行会对不齐
|
||||
setLabelLength = fieldExtendJson.labelLength > setLabelLength ? fieldExtendJson.labelLength : setLabelLength;
|
||||
} else {
|
||||
setLabelLength = fieldExtendJson.labelLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20231205---for:【QQYUN-7140】online label默认显示6个
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20231205---for:【QQYUN-7140】online label默认显示6个
|
||||
// 配置中没有设置,读取默认的长度
|
||||
if (setLabelLength == -1) {
|
||||
setLabelLength = LABELLENGTH;
|
||||
} else {
|
||||
// update-begin--author:liaozhiyang---date:20240517---for:【TV360X-98】label展示的文字必须和labelLength配置一致
|
||||
arr.forEach(item=>{
|
||||
item.labelLength = setLabelLength;
|
||||
})
|
||||
// update-end--author:liaozhiyang---date:20240517---for:【TV360X-98】label展示的文字必须和labelLength配置一致
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20231205---for:【QQYUN-7140】online label默认显示6个
|
||||
arr.sort(function (a, b) {
|
||||
return a.order - b.order;
|
||||
});
|
||||
let schemaArray = [];
|
||||
if (arr.length > 2) {
|
||||
toggleButtonShow.value = true;
|
||||
}
|
||||
let hideFieldName = [];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let item = arr[i];
|
||||
item.setFormRef(onlineQueryFormRef);
|
||||
item.noChange();
|
||||
item.asSearchForm();
|
||||
if (i > 1) {
|
||||
hideFieldName.push(item.field);
|
||||
item.isHidden();
|
||||
}
|
||||
//update-begin-author:taoyan date:2022-10-24 for: VUEN-2493【优化】online默认查询条件太宽了,参考online报表
|
||||
let tempSchema = item.getFormItemSchema();
|
||||
if(item.slot == 'groupDatetime'){
|
||||
// update-begin--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换
|
||||
//如果是时间类型 重新设定colprops (小于3个时才重置,否则多行会导致对不齐)
|
||||
arr.length <= 3 && (tempSchema['colProps'] = { xs:24, sm: 24, md: 12, lg:8, xl:8 })
|
||||
// update-end--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240522---for:【TV360X-250】查询区域把Switch开关组件改成select组件
|
||||
if (tempSchema.component === 'JSwitch') {
|
||||
const componentProps = tempSchema.componentProps ?? {};
|
||||
tempSchema.componentProps = { ...componentProps, query: true };
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240522---for:【TV360X-250】查询区域把Switch开关组件改成select组件
|
||||
linkTableCard2Select(tempSchema);
|
||||
// update-begin--author:liaozhiyang---date:20240530---for:【TV360X-389】普通查询关联记录去掉编辑按钮
|
||||
if (tempSchema.component === 'LinkTableSelect') {
|
||||
let componentProps = tempSchema.componentProps ?? {};
|
||||
tempSchema.componentProps = { ...componentProps, editBtnShow: false };
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240530---for:【TV360X-389】普通查询关联记录去掉编辑按钮
|
||||
// update-begin--author:liaozhiyang---date:20240614---for:【TV360X-1231】查询区域有的下拉组件显示不全
|
||||
const compProps = tempSchema.componentProps ?? {};
|
||||
if (!compProps.getPopupContainer) {
|
||||
tempSchema.componentProps = { ...compProps, getPopupContainer: () => document.body };
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240614---for:【TV360X-1231】查询区域有的下拉组件显示不全
|
||||
// update-begin--author:liaozhiyang---date:20240725---for:【TV360X-1857】online查询增加模糊查询
|
||||
const fieldData = formProperties[tempSchema.field] ?? {};
|
||||
// 【TV360X-1966】页面属性控件配置了文本框且字段类型是string,个性化查询配置了用户组件,用户组件不生效
|
||||
if (fieldData.mode == 'like' && fieldData.view === 'text' && fieldData.originView === 'text') {
|
||||
tempSchema.component = 'JInput';
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240725---for:【TV360X-1857】online查询增加模糊查询
|
||||
schemaArray.push(tempSchema);
|
||||
//update-end-author:taoyan date:2022-10-24 for: VUEN-2493【优化】online默认查询条件太宽了,参考online报表
|
||||
}
|
||||
hideList.value = hideFieldName;
|
||||
formSchemas.value = schemaArray;
|
||||
//设置表单默认值
|
||||
defaultValues.config = { ...configValue };
|
||||
defaultValues.status = !defaultValues.status;
|
||||
// update-begin--author:liaozhiyang---date:20231204---for:【QQYUN-7140】online label默认显示6个
|
||||
setTimeout(() => {
|
||||
// 14是文字size,24是间隙
|
||||
const w = setLabelLength * 14 + setLabelLength + 24;
|
||||
formLabelWidth.value = w;
|
||||
}, 0);
|
||||
// update-end--author:liaozhiyang---date:20231204---for:【QQYUN-7140】online label默认显示6个
|
||||
}
|
||||
/**
|
||||
* 2024-05-31
|
||||
* liaozhiyang
|
||||
* 【TV360X-415】个性化查询支持年、月、周、季度.
|
||||
* 解析特定view(组件)字段的值,把view字段值为date_year、date_month、date_week、date_quarter
|
||||
* 改成date,并组装fieldExtendJson
|
||||
*/
|
||||
const analysisComponent = (res) => {
|
||||
const properties = res.properties;
|
||||
if (properties) {
|
||||
Object.entries(properties).forEach(([key, value]) => {
|
||||
const data = value;
|
||||
if (['date_year', 'date_month', 'date_week', 'date_quarter'].includes(data.view)) {
|
||||
const fieldExtendJson = data.fieldExtendJson ? JSON.parse(data.fieldExtendJson) : {};
|
||||
fieldExtendJson.picker = data.view.split('_')[1];
|
||||
data.fieldExtendJson = JSON.stringify(fieldExtendJson);
|
||||
data.view = 'date';
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
async function resetForm() {
|
||||
let json = await loadQueryInfo();
|
||||
// update-begin--author:liaozhiyang---date:20240531---for:【TV360X-415】个性化查询支持年、月、周、季度
|
||||
analysisComponent(json);
|
||||
// update-end--author:liaozhiyang---date:20240318---for:【TV360X-415】个性化查询支持年、月、周、季度
|
||||
// update-begin--author:liaozhiyang---date:20240524---for:【TV360X-516】高级查询过滤掉不支持查询的组件
|
||||
// filterComponent(json);
|
||||
// update-end--author:liaozhiyang---date:20240524---for:【TV360X-516】高级查询过滤掉不支持查询的组件
|
||||
// 获取所有字段配置 通过事件回传给高级查询组件
|
||||
let allFields = getAllFields(json);
|
||||
emit('loaded', json);
|
||||
// 获取查询条件表单页面配置
|
||||
let { formProperties, hasField } = getQueryFormProperties(allFields, json);
|
||||
if (hasField == false) {
|
||||
formSchemas.value = [];
|
||||
return;
|
||||
}
|
||||
// 获取表单配置formSchemas
|
||||
await initSchemas(formProperties);
|
||||
}
|
||||
/**
|
||||
* 2024-05-24
|
||||
* liaozhiyang
|
||||
* 过滤掉不支持查询的组件(图片、文件、密码、关联记录、联动)
|
||||
*/
|
||||
const filterComponent = (data) => {
|
||||
const { properties = {} } = data;
|
||||
Object.entries(properties).forEach(([field, value]) => {
|
||||
if (value.view === 'table') {
|
||||
filterComponent(value);
|
||||
}
|
||||
if (['image', 'password', 'file', 'link_table', 'link_down'].includes(value.view)) {
|
||||
delete properties[field];
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 设置表单的值
|
||||
*/
|
||||
async function customSetFieldsValue(rawValues) {
|
||||
await getRefPromise(onlineQueryFormRef);
|
||||
console.log('rawValues', rawValues);
|
||||
// update-begin--author:liaozhiyang---date:20240618---for:online普通查询默认值范围查询不好使
|
||||
const values = transformGroupDefValus(rawValues);
|
||||
await setFieldsValue(values);
|
||||
// update-end--author:liaozhiyang---date:20240618---for:online普通查询默认值范围查询不好使
|
||||
if (Object.keys(values).length > 0) {
|
||||
doSearch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转化
|
||||
*/
|
||||
function getQueryFormProperties(allFields, json) {
|
||||
const { searchFieldList, joinQuery, table } = json;
|
||||
let hasField = false;
|
||||
let formProperties = {};
|
||||
if (allFields) {
|
||||
Object.keys(allFields).map((field) => {
|
||||
if (searchFieldList.indexOf(field) >= 0) {
|
||||
//只找需要查询的字段
|
||||
if (joinQuery == true) {
|
||||
//判断是不是联合查询
|
||||
if (field.indexOf('@') < 0) {
|
||||
//没有@说明是主表字段, 手动拼接上@
|
||||
formProperties[table + '@' + field] = allFields[field];
|
||||
hasField = true;
|
||||
} else {
|
||||
//有@说明是子表字段,直接获取
|
||||
formProperties[field] = allFields[field];
|
||||
hasField = true;
|
||||
}
|
||||
} else {
|
||||
// 不是联合查询 只查主表字段
|
||||
if (field.indexOf('@') < 0) {
|
||||
formProperties[field] = allFields[field];
|
||||
hasField = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
formProperties,
|
||||
hasField,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询条件表单配置
|
||||
* json结构:
|
||||
* 主表字段1:{配置1}
|
||||
* 主表字段2:{配置2}
|
||||
* 子表名@子表字段1:{子表字段配置1}
|
||||
* 子表名@子表字段2:{子表字段配置2}
|
||||
*/
|
||||
function getAllFields(json) {
|
||||
// 获取所有配置 查询字段 是否联合查询
|
||||
const { properties, searchFieldList, joinQuery, table } = json;
|
||||
let allFields = {};
|
||||
let order = 1;
|
||||
let hasField = false;
|
||||
Object.keys(properties).map((field) => {
|
||||
let item = properties[field];
|
||||
if (item.view == 'table') {
|
||||
// 子表字段
|
||||
// 联合查询开启才需要子表字段作为查询条件
|
||||
let subProps = item['properties'];
|
||||
let subTableOrder = order * 100;
|
||||
Object.keys(subProps).map((subField) => {
|
||||
let subItem = subProps[subField];
|
||||
// 保证排序统一
|
||||
subItem['order'] = subTableOrder + Number(subItem['order']);
|
||||
let subFieldKey = field + '@' + subField;
|
||||
allFields[subFieldKey] = subItem;
|
||||
});
|
||||
order++;
|
||||
} else {
|
||||
// 主表字段
|
||||
item['order'] = Number(item['order']);
|
||||
allFields[field] = item;
|
||||
}
|
||||
});
|
||||
return allFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询返回数据格式, 需要经过getQueryFormProperties转成表单配置
|
||||
* json结构:
|
||||
* table(表名)
|
||||
* title(表描述)
|
||||
* properties(表字段)
|
||||
* field1
|
||||
* field2
|
||||
* fieldxxx
|
||||
* sub-table1(子表名1)
|
||||
* title(子表描述1)
|
||||
* properties(子表字段)
|
||||
* sub-table2(子表名2)
|
||||
* title(子表描述2)
|
||||
* properties(子表字段)
|
||||
*/
|
||||
function loadQueryInfo() {
|
||||
let url = `${LOAD_URL}${props.id}`;
|
||||
return new Promise((resolve) => {
|
||||
defHttp
|
||||
.get({ url }, { isTransformResponse: false })
|
||||
.then((res) => {
|
||||
// console.log("-online列表查询条件获取配置", res);
|
||||
if (res.success) {
|
||||
resolve(res.result);
|
||||
} else {
|
||||
resolve(false);
|
||||
$message.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
$message.warning('获取查询条件失败!');
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, updateSchema, getFieldsValue }] = useForm({
|
||||
name: 'online-query-form',
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: baseColProps,
|
||||
autoSubmitOnEnter: true,
|
||||
labelWidth: formLabelWidth,
|
||||
wrapperCol: null,
|
||||
submitFunc() {
|
||||
//update-begin---author:wangshuai---date:2025-10-11---for:【issues/8790】online 表单重大 bug,影响配置了查询 的所有表单---
|
||||
//doSearch();
|
||||
//update-end---author:wangshuai---date:2025-10-11---for:【issues/8790】online 表单重大 bug,影响配置了查询 的所有表单---
|
||||
},
|
||||
/* labelCol: ONL_QUERY_LABEL_COL,
|
||||
wrapperCol: ONL_QUERY_WRAPPER_COL*/
|
||||
});
|
||||
|
||||
/**
|
||||
* 执行查询
|
||||
*/
|
||||
function doSearch() {
|
||||
let formValues = getFieldsValue();
|
||||
// update-begin--author:liaozhiyang---date:20240517---for:【TV360X-28】年,年月,周查询出结果不准
|
||||
transformDateValus(formValues);
|
||||
// update-end--author:liaozhiyang---date:20240517---for:【TV360X-28】年,年月,周查询出结果不准
|
||||
// update-begin--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换
|
||||
transformGroupValus(formValues);
|
||||
// update-end--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换
|
||||
// 还需要把地址栏参数添加进去
|
||||
let data = Object.assign({}, toRaw(defaultValues.param), changeDataIfArray2String(formValues));
|
||||
emit('search', data, true);
|
||||
}
|
||||
/**
|
||||
* 2024-06-18
|
||||
* liaozhiyang
|
||||
* online普通查询默认值范围查询不好使
|
||||
* */
|
||||
const transformGroupDefValus = (obj) => {
|
||||
const values = { ...obj };
|
||||
const groupSchemas = formSchemas.value.filter((item) => ['groupTime', 'groupDatetime', 'groupNumber', 'groupDate'].includes(item.slot));
|
||||
if (groupSchemas.length) {
|
||||
Object.keys(values).forEach((filed) => {
|
||||
let key;
|
||||
const findItem = groupSchemas.find((item) => {
|
||||
if (item.field === filed) {
|
||||
key = filed;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (findItem) {
|
||||
const value = values[key];
|
||||
if (typeof value === 'string') {
|
||||
const arr = value.split(',');
|
||||
values[key] = [...arr];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return values;
|
||||
};
|
||||
|
||||
/**
|
||||
* 2024-05-20
|
||||
* liaozhiyang
|
||||
* 【TV360X-213】把groupDatetime,groupTime,groupDate,groupNumber等范围字段分割成两个字段
|
||||
*/
|
||||
const transformGroupValus = (values) => {
|
||||
if (values) {
|
||||
const groupSchemas = formSchemas.value.filter((item) => ['groupTime', 'groupDatetime', 'groupDate', 'groupNumber'].includes(item.slot));
|
||||
if (groupSchemas.length) {
|
||||
Object.keys(values).forEach((filed) => {
|
||||
let key;
|
||||
const findItem = groupSchemas.find((item) => {
|
||||
if (item.field === filed) {
|
||||
key = filed;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (findItem) {
|
||||
const value = values[key];
|
||||
if (typeof value === 'string') {
|
||||
const arr = value.split(',');
|
||||
values[`${key}_begin`] = arr[0];
|
||||
values[`${key}_end`] = arr[1];
|
||||
delete values[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 2024-05-20
|
||||
* liaozhiyang
|
||||
* 把年,年月,周等时间重置到当前格式的第一天,因为存的时候也是第一天 (【TV360X-180】兼容时间范围)
|
||||
*/
|
||||
const transformDateValus = (values) => {
|
||||
const dateSchemas = formSchemas.value.filter((item) => item.componentProps?.picker && item.componentProps.picker != 'default');
|
||||
if (dateSchemas.length) {
|
||||
Object.keys(values).forEach((filed) => {
|
||||
let key;
|
||||
const findItem = dateSchemas.find((item) => {
|
||||
if (item.field === filed || `${item.field}_begin` === filed || `${item.field}_end` === filed) {
|
||||
key = filed;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (findItem) {
|
||||
const value = values[key];
|
||||
if (value) {
|
||||
// update-begin--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换
|
||||
const auto = (value, key, isEnd) => {
|
||||
const picker = findItem.componentProps.picker;
|
||||
if (picker === 'year') {
|
||||
if (isEnd) {
|
||||
values[key] = dayjs(value).endOf('year').format('YYYY-MM-DD');
|
||||
} else {
|
||||
values[key] = dayjs(value).startOf('year').format('YYYY-MM-DD');
|
||||
}
|
||||
} else if (picker === 'month') {
|
||||
if (isEnd) {
|
||||
values[key] = dayjs(value).endOf('month').format('YYYY-MM-DD');
|
||||
} else {
|
||||
values[key] = dayjs(value).startOf('month').format('YYYY-MM-DD');
|
||||
}
|
||||
} else if (picker === 'week') {
|
||||
if (isEnd) {
|
||||
values[key] = dayjs(value).endOf('week').format('YYYY-MM-DD');
|
||||
} else {
|
||||
values[key] = dayjs(value).startOf('week').format('YYYY-MM-DD');
|
||||
}
|
||||
} else if (picker === 'quarter') {
|
||||
if (isEnd) {
|
||||
values[key] = dayjs(value).endOf('quarter').format('YYYY-MM-DD');
|
||||
} else {
|
||||
values[key] = dayjs(value).startOf('quarter').format('YYYY-MM-DD');
|
||||
}
|
||||
}
|
||||
};
|
||||
if (findItem?.slot === 'groupDate') {
|
||||
const arr = value.split(',');
|
||||
auto(arr[0], `${key}_begin`, false);
|
||||
auto(arr[1], `${key}_end`, true);
|
||||
delete values[key];
|
||||
} else {
|
||||
auto(value, key, false);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240530---for:【TV360X-213】普通查询日期数值组件更换
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置是将 查询控件的值 重置到默认值
|
||||
* 2024-05-23
|
||||
* liaozhiyang
|
||||
* 【TV360X-124】提供clearSearch方法回到初始状态
|
||||
*/
|
||||
async function clearSearch() {
|
||||
await resetFields();
|
||||
const { config, param } = toRaw(defaultValues);
|
||||
let rawValues = Object.assign({}, config, param);
|
||||
if (Object.keys(rawValues).length > 0) {
|
||||
await setFieldsValue(rawValues);
|
||||
}
|
||||
return rawValues;
|
||||
}
|
||||
|
||||
async function resetSearch() {
|
||||
const rawValues = await clearSearch();
|
||||
emit('search', rawValues, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 有些数据是数组格式的 强转成字符串
|
||||
*/
|
||||
function changeDataIfArray2String(data) {
|
||||
Object.keys(data).map((k) => {
|
||||
if (data[k]) {
|
||||
if (data[k] instanceof Array) {
|
||||
data[k] = data[k].join(',');
|
||||
}
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => toggleSearchStatus.value,
|
||||
(status) => {
|
||||
let names = hideList.value;
|
||||
if (names && names.length > 0) {
|
||||
let arr = [];
|
||||
for (let name of names) {
|
||||
arr.push({
|
||||
field: name,
|
||||
show: status,
|
||||
});
|
||||
}
|
||||
updateSchema(arr);
|
||||
}
|
||||
},
|
||||
{ immediate: false }
|
||||
);
|
||||
/**
|
||||
* 2024-05-30
|
||||
* liaozhiyang
|
||||
* 【TV360X-392】日期placeholder修改
|
||||
* */
|
||||
const getGroupDatePlaceholder = (data) => {
|
||||
let result = ['开始日期', '结束日期'];
|
||||
console.log(data);
|
||||
if (data?.picker) {
|
||||
switch (data?.picker) {
|
||||
case 'year':
|
||||
result = ['开始年份', '结束年份'];
|
||||
break;
|
||||
case 'month':
|
||||
result = ['开始月份', '结束月份'];
|
||||
break;
|
||||
case 'week':
|
||||
result = ['开始周', '结束周'];
|
||||
break;
|
||||
case 'quarter':
|
||||
result = ['开始季度', '结束季度'];
|
||||
break;
|
||||
default:
|
||||
result = ['开始日期', '结束日期'];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return {
|
||||
onlineQueryFormRef,
|
||||
registerForm,
|
||||
initDefaultValues,
|
||||
toggleButtonShow,
|
||||
toggleSearchStatus,
|
||||
doSearch,
|
||||
resetSearch,
|
||||
queryParams,
|
||||
formSchemas,
|
||||
clearSearch,
|
||||
getGroupDatePlaceholder,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.group-query-string {
|
||||
width: 20px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
// 查询条件的边距要和列表对齐,所以查询条件的边距要设为0
|
||||
.jeecg-basic-table-form-container.p-0 {
|
||||
padding: 0;
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240514---for:【QQYUN-9241】form表单上下间距大点
|
||||
.jeecg-basic-table-form-container {
|
||||
:deep(.ant-form-item) {
|
||||
&:not(.ant-form-item-with-help) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240514---for:【QQYUN-9241】form表单上下间距大点
|
||||
.online-query-form {
|
||||
:deep(.ant-form) {
|
||||
max-height: 40vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,415 @@
|
||||
<!-- 此文件已没有使用的地方 -->
|
||||
<template>
|
||||
<a-form-item :labelCol="labelCol" :class="'jeecg-online-search'">
|
||||
<template #label>
|
||||
<span :title="item.label" class="label-text">{{ item.label }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 1.日期 -->
|
||||
<template v-if="item.view == 'date'">
|
||||
<template v-if="single_mode === item.mode">
|
||||
<a-date-picker
|
||||
style="width: 100%"
|
||||
:showTime="false"
|
||||
valueFormat="YYYY-MM-DD"
|
||||
:placeholder="'请选择' + item.label"
|
||||
v-model:value="innerValue"
|
||||
></a-date-picker>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-date-picker
|
||||
:showTime="false"
|
||||
valueFormat="YYYY-MM-DD"
|
||||
placeholder="开始日期"
|
||||
v-model:value="beginValue"
|
||||
style="width: calc(50% - 15px)"
|
||||
></a-date-picker>
|
||||
<span class="group-query-strig">~</span>
|
||||
<a-date-picker
|
||||
:showTime="false"
|
||||
valueFormat="YYYY-MM-DD"
|
||||
placeholder="结束日期"
|
||||
v-model:value="endValue"
|
||||
style="width: calc(50% - 15px)"
|
||||
></a-date-picker>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 2.时间 -->
|
||||
<template v-else-if="item.view == 'datetime'">
|
||||
<template v-if="single_mode === item.mode">
|
||||
<a-date-picker
|
||||
style="width: 100%"
|
||||
:showTime="true"
|
||||
valueFormat="YYYY-MM-DD hh:mm:ss"
|
||||
:placeholder="'请选择' + item.label"
|
||||
v-model:value="innerValue"
|
||||
></a-date-picker>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-date-picker
|
||||
:showTime="true"
|
||||
valueFormat="YYYY-MM-DD hh:mm:ss"
|
||||
placeholder="开始时间"
|
||||
v-model:value="beginValue"
|
||||
style="width: calc(50% - 15px)"
|
||||
></a-date-picker>
|
||||
<span class="group-query-strig">~</span>
|
||||
<a-date-picker
|
||||
:showTime="true"
|
||||
valueFormat="YYYY-MM-DD hh:mm:ss"
|
||||
placeholder="结束时间"
|
||||
v-model:value="endValue"
|
||||
style="width: calc(50% - 15px)"
|
||||
></a-date-picker>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 3 TODO 时分秒 -->
|
||||
|
||||
<!-- 4.简单下拉框 -->
|
||||
<template v-else-if="isEasySelect()">
|
||||
<JDictSelectTag v-if="item.config === '1'" :placeholder="'请选择' + item.label" v-model:value="innerValue" :dictCode="getDictCode()" />
|
||||
<a-select v-else :placeholder="'请选择' + item.label" v-model:value="innerValue">
|
||||
<template v-for="(obj, index) in dictOptions[getDictOptionKey(item)]" :key="index">
|
||||
<a-select-option :value="obj.value"> {{ obj.text }}</a-select-option>
|
||||
</template>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<!-- 5.下拉树 -->
|
||||
<template v-else-if="item.view === 'sel_tree'">
|
||||
<JTreeSelect
|
||||
:placeholder="'请选择' + item.label"
|
||||
v-model:value="innerValue"
|
||||
:dict="item.dict"
|
||||
:pidField="item.pidField"
|
||||
:pidValue="item.pidValue"
|
||||
:hasChildField="item.hasChildField"
|
||||
load-triggle-change
|
||||
>
|
||||
</JTreeSelect>
|
||||
</template>
|
||||
|
||||
<!-- 6.分类树 -->
|
||||
<template v-else-if="item.view === 'cat_tree'">
|
||||
<JCategorySelect
|
||||
@change="handleCategoryTreeChange"
|
||||
:loadTriggleChange="true"
|
||||
:pcode="item.pcode"
|
||||
v-model:value="innerValue"
|
||||
:placeholder="'请选择' + item.label"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 7.下拉搜索 -->
|
||||
<template v-else-if="item.view === 'sel_search'">
|
||||
<JDictSelectTag v-if="item.config === '1'" v-model:value="innerValue" :placeholder="'请选择' + item.label" :dict="getDictCode()" />
|
||||
<JOnlineSearchSelect v-else v-model:value="innerValue" :placeholder="'请选择' + item.label" :sql="getSqlByDictCode()" />
|
||||
</template>
|
||||
|
||||
<!-- 8.用户 -->
|
||||
<JSelectUser
|
||||
v-else-if="item.view == 'sel_user'"
|
||||
v-bind="userSelectProp"
|
||||
v-model:value="innerValue"
|
||||
:placeholder="'请选择' + item.label"
|
||||
></JSelectUser>
|
||||
|
||||
<!-- 9.部门 -->
|
||||
<JSelectDept
|
||||
v-else-if="item.view == 'sel_depart'"
|
||||
:showButton="false"
|
||||
v-bind="depSelectProp"
|
||||
v-model:value="innerValue"
|
||||
:placeholder="'请选择' + item.label"
|
||||
/>
|
||||
|
||||
<!-- 10.popup -->
|
||||
<JPopup
|
||||
v-else-if="item.view == 'popup'"
|
||||
:placeholder="'请选择' + item.label"
|
||||
v-model:value="innerValue"
|
||||
:code="item.dictTable"
|
||||
:setFieldsValue="setFieldsValue"
|
||||
:field-config="getPopupFieldConfig(item)"
|
||||
:multi="true"
|
||||
>
|
||||
</JPopup>
|
||||
|
||||
<!-- 11.省市区 -->
|
||||
<JAreaSelect v-else-if="item.view == 'pca'" :placeholder="'请选择' + item.label" v-model:value="innerValue" />
|
||||
|
||||
<!-- 12.下拉多选 -->
|
||||
<template v-else-if="item.view == 'checkbox' || item.view == 'list_multi'" :label="item.label">
|
||||
<JSelectMultiple :dictCode="getDictCode()" :placeholder="'请选择' + item.label" v-model:value="innerValue"></JSelectMultiple>
|
||||
|
||||
<!--<JDictSelectTag mode="multiple" @change="handleSelectChange" :dictCode="getDictCode()"/>-->
|
||||
</template>
|
||||
|
||||
<!-- 13.普通输入框 -->
|
||||
<template v-else>
|
||||
<template v-if="single_mode === item.mode">
|
||||
<a-input :placeholder="'请选择' + item.label" v-model:value="innerValue"></a-input>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-input placeholder="开始值" v-model:value="beginValue" style="width: calc(50% - 15px)"></a-input>
|
||||
<span class="group-query-strig">~</span>
|
||||
<a-input placeholder="结束值" v-model:value="endValue" style="width: calc(50% - 15px)"></a-input>
|
||||
</template>
|
||||
</template>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, nextTick, ref, unref, watch, toRaw } from 'vue';
|
||||
import {
|
||||
JDictSelectTag,
|
||||
JTreeSelect,
|
||||
JSearchSelect,
|
||||
JCategorySelect,
|
||||
JSelectUserByDept,
|
||||
JSelectDept,
|
||||
JPopup,
|
||||
JAreaLinkage,
|
||||
JSelectUser,
|
||||
JSelectMultiple,
|
||||
JAreaSelect,
|
||||
FormActionType,
|
||||
} from '/@/components/Form';
|
||||
import JOnlineSearchSelect from '../../auto/comp/JOnlineSearchSelect.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OnlineSearchFormItem',
|
||||
components: {
|
||||
JOnlineSearchSelect,
|
||||
JDictSelectTag,
|
||||
JTreeSelect,
|
||||
JCategorySelect,
|
||||
JSelectUser,
|
||||
JSelectUserByDept,
|
||||
JSelectDept,
|
||||
JPopup,
|
||||
JAreaLinkage,
|
||||
JAreaSelect,
|
||||
JSelectMultiple,
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
required: true,
|
||||
},
|
||||
dictOptions: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
required: false,
|
||||
},
|
||||
onlineForm: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:value', 'change'],
|
||||
setup(props, { emit }) {
|
||||
// 定义查询条件 文本label的最大宽度 比起单纯的控制字体个数更好
|
||||
const labelTextMaxWidth = '120px';
|
||||
const labelCol = {
|
||||
style: {
|
||||
'max-width': labelTextMaxWidth,
|
||||
},
|
||||
};
|
||||
const single_mode = 'single';
|
||||
let innerValue = ref<string | undefined | []>('');
|
||||
let beginValue = ref('');
|
||||
let endValue = ref('');
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
if (isEasySelect()) {
|
||||
// 下拉框这里设置空数组 不知道为什么会有警告
|
||||
innerValue.value = !!props.value ? props.value : undefined;
|
||||
} else {
|
||||
innerValue.value = props.value;
|
||||
}
|
||||
if (!props.value) {
|
||||
beginValue.value = '';
|
||||
endValue.value = '';
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
innerValue,
|
||||
(newVal) => {
|
||||
console.log('innerValue-change', newVal);
|
||||
emit('update:value', newVal);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(beginValue, (newVal) => {
|
||||
emit('change', props.item.field + '_begin', newVal);
|
||||
emit('update:value', '1');
|
||||
});
|
||||
|
||||
watch(endValue, (newVal) => {
|
||||
emit('change', props.item.field + '_end', newVal);
|
||||
emit('update:value', '1');
|
||||
});
|
||||
|
||||
function getDictOptionKey(item) {
|
||||
console.log('ddictOptions', props.dictOptions);
|
||||
if (item.dbField) {
|
||||
return item.dbField;
|
||||
} else {
|
||||
return item.field;
|
||||
}
|
||||
}
|
||||
|
||||
function isEasySelect() {
|
||||
let item = props.item;
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
return item.view == 'list' || item.view == 'radio' || item.view == 'switch';
|
||||
}
|
||||
|
||||
function getDictCode() {
|
||||
let item = props.item;
|
||||
if (item.dictTable && item.dictTable.length > 0) {
|
||||
return item.dictTable + ',' + item.dictText + ',' + item.dictCode;
|
||||
} else {
|
||||
return item.dictCode;
|
||||
}
|
||||
}
|
||||
|
||||
function getSqlByDictCode() {
|
||||
let item = props.item;
|
||||
let { dictTable, dictCode, dictText } = item;
|
||||
let temp = dictTable.toLowerCase();
|
||||
let arr = temp.split('where');
|
||||
let condition = '';
|
||||
if (arr.length > 1) {
|
||||
condition = ' where' + arr[1];
|
||||
}
|
||||
let sql = 'select ' + dictCode + " as 'value', " + dictText + " as 'text' from " + arr[0] + condition;
|
||||
console.log('sql', sql);
|
||||
return sql;
|
||||
}
|
||||
|
||||
function getPopupFieldConfig(item) {
|
||||
let { dictText: destFields, dictCode: orgFields } = item;
|
||||
if (!destFields || destFields.length == 0) {
|
||||
return [];
|
||||
}
|
||||
let arr1 = destFields.split(',');
|
||||
let arr2 = orgFields.split(',');
|
||||
let config: any[] = [];
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
config.push({
|
||||
target: arr1[i],
|
||||
source: arr2[i],
|
||||
});
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
function setFieldsValue<T>(values: T) {
|
||||
let { dictText: destFields } = props.item;
|
||||
let arr1 = destFields.split(',');
|
||||
let field = arr1[0];
|
||||
emit('change', field, values[field]);
|
||||
}
|
||||
|
||||
function handleCategoryTreeChange(value) {
|
||||
emit('update:value', value);
|
||||
}
|
||||
|
||||
function getComponentProps(item, labelKey, rowKey) {
|
||||
let props = {
|
||||
labelKey,
|
||||
rowKey,
|
||||
};
|
||||
let fieldExtendJson = item.fieldExtendJson;
|
||||
if (fieldExtendJson) {
|
||||
if (typeof fieldExtendJson == 'string') {
|
||||
let json = JSON.parse(fieldExtendJson);
|
||||
let extend = { ...json };
|
||||
if (extend.text) {
|
||||
props['labelKey'] = extend.text;
|
||||
}
|
||||
if (extend.store) {
|
||||
props['rowKey'] = extend.store;
|
||||
}
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
let userSelectProp = getComponentProps(props.item, 'realname', 'username');
|
||||
console.log('userSelectProp', userSelectProp);
|
||||
let depSelectProp = getComponentProps(props.item, 'departName', 'id');
|
||||
|
||||
function handleSelectChange(array) {
|
||||
if (array && array.length > 0) {
|
||||
emit('update:value', array.join(','));
|
||||
} else {
|
||||
emit('update:value', '');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getPopupFieldConfig,
|
||||
userSelectProp,
|
||||
depSelectProp,
|
||||
handleSelectChange,
|
||||
setFieldsValue,
|
||||
innerValue,
|
||||
beginValue,
|
||||
endValue,
|
||||
isEasySelect,
|
||||
getDictOptionKey,
|
||||
getDictCode,
|
||||
labelTextMaxWidth,
|
||||
labelCol,
|
||||
single_mode,
|
||||
getSqlByDictCode,
|
||||
handleCategoryTreeChange,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.group-query-strig {
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
/* 查询条件左对齐样式设置 */
|
||||
.jeecg-online-search :deep(.ant-form-item-label) {
|
||||
flex: 0 0 auto !important;
|
||||
width: auto;
|
||||
}
|
||||
.jeecg-online-search :deep(.ant-form-item-control) {
|
||||
max-width: 100%;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
/* label显示宽度 超出显示... */
|
||||
.jeecg-online-search :deep(.label-text) {
|
||||
max-width: v-bind(labelTextMaxWidth);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<!-- 级联下拉框 form组件 暂且只在online使用 不对外提供api -->
|
||||
<a-select show-search :filter-option="filterOption" :placeholder="placeholder" :value="selectedValue" @change="handleChange" allowClear style="width: 100%">
|
||||
<a-select-option v-for="(item, index) in dictOptions" :key="index" :value="item.store">
|
||||
<span style="display: inline-block; width: 100%" :title="item.label">{{ item.label }}</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch, ref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
/**获取下拉选项*/
|
||||
const SELECT_OPTIONS_URL = '/online/cgform/api/querySelectOptions';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OnlineSelectCascade',
|
||||
props: {
|
||||
table: { type: String, default: '' },
|
||||
txt: { type: String, default: '' },
|
||||
store: { type: String, default: '' },
|
||||
idField: { type: String, default: '' },
|
||||
pidField: { type: String, default: '' },
|
||||
pidValue: { type: String, default: '-1' },
|
||||
origin: { type: Boolean, default: false },
|
||||
condition: { type: String, default: '' },
|
||||
value: { type: String, default: '' },
|
||||
isNumber: { type: Boolean, default: false },
|
||||
placeholder: { type: String, default: '请选择' },
|
||||
},
|
||||
emits: ['change', 'next'],
|
||||
setup(props, { emit }) {
|
||||
const { createMessage: $message } = useMessage();
|
||||
// 选中值
|
||||
const selectedValue = ref<any>('');
|
||||
// 选项数组
|
||||
const dictOptions = ref<any[]>([]);
|
||||
const optionsLoad = ref(true);
|
||||
// 选项改变事件
|
||||
function handleChange(value) {
|
||||
console.log('handleChange', value);
|
||||
// 这个value是 存储的值 实际还需要获取id值
|
||||
let temp = value || '';
|
||||
emit('change', temp);
|
||||
valueChangeThenEmitNext(temp);
|
||||
}
|
||||
|
||||
// 第一个节点 选项加载走condition
|
||||
watch(
|
||||
() => props.condition,
|
||||
(val) => {
|
||||
optionsLoad.value = true;
|
||||
if (val) {
|
||||
loadOptions();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 被联动节点 选项加载走pidValue
|
||||
watch(
|
||||
() => props.pidValue,
|
||||
(val) => {
|
||||
if (val === '-1') {
|
||||
dictOptions.value = [];
|
||||
} else {
|
||||
loadOptions();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 值回显
|
||||
watch(
|
||||
() => props.value,
|
||||
(newVal, oldVal) => {
|
||||
console.log('值改变事件', newVal, oldVal);
|
||||
if (!newVal) {
|
||||
// value不存在的时候--
|
||||
selectedValue.value = [];
|
||||
if (oldVal) {
|
||||
// 如果oldVal存在, 需要往上抛事件
|
||||
emit('change', '');
|
||||
emit('next', '-1');
|
||||
}
|
||||
} else {
|
||||
// value存在的时候
|
||||
selectedValue.value = newVal;
|
||||
}
|
||||
if (newVal && !oldVal) {
|
||||
// 有新值没有旧值 表单第一次加载赋值 需要往外抛一个事件 触发下级options的加载
|
||||
handleFirstValueSetting(newVal);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**
|
||||
* 第一次加载赋值
|
||||
*/
|
||||
async function handleFirstValueSetting(value) {
|
||||
if (props.idField === props.store) {
|
||||
// 如果id字段就是存储字段 那么可以不用调用请求
|
||||
emit('next', value);
|
||||
} else {
|
||||
if (props.origin === true) {
|
||||
// 如果是联动组件的第一个组件,等待options加载完后从options中取值
|
||||
await getSelfOptions();
|
||||
valueChangeThenEmitNext(value);
|
||||
} else {
|
||||
// 如果是联动组件的后续组件,根据选中的value加载一遍数据
|
||||
let arr = await loadValueText();
|
||||
valueChangeThenEmitNext(value, arr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadOptions() {
|
||||
let params = getQueryParams();
|
||||
if (props.origin === true) {
|
||||
params['condition'] = props.condition;
|
||||
} else {
|
||||
params['pidValue'] = props.pidValue;
|
||||
}
|
||||
console.log('请求参数', params);
|
||||
dictOptions.value = [];
|
||||
defHttp.get({ url: SELECT_OPTIONS_URL, params }, { isTransformResponse: false }).then((res) => {
|
||||
if (res.success) {
|
||||
dictOptions.value = [...res.result];
|
||||
console.log('请求结果', res.result, dictOptions);
|
||||
} else {
|
||||
$message.warning('联动组件数据加载失败,请检查配置!');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getQueryParams() {
|
||||
let params = {
|
||||
table: props.table,
|
||||
txt: props.txt,
|
||||
key: props.store,
|
||||
idField: props.idField,
|
||||
pidField: props.pidField,
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
function loadValueText() {
|
||||
return new Promise((resolve) => {
|
||||
if (!props.value) {
|
||||
selectedValue.value = [];
|
||||
resolve([]);
|
||||
} else {
|
||||
let params = getQueryParams();
|
||||
if (props.isNumber === true) {
|
||||
params['condition'] = `${props.store} = ${props.value}`;
|
||||
} else {
|
||||
params['condition'] = `${props.store} = '${props.value}'`;
|
||||
}
|
||||
defHttp.get({ url: SELECT_OPTIONS_URL, params }, { isTransformResponse: false }).then((res) => {
|
||||
if (res.success) {
|
||||
resolve(res.result);
|
||||
} else {
|
||||
$message.warning('联动组件数据加载失败,请检查配置!');
|
||||
resolve([]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下拉选项
|
||||
*/
|
||||
function getSelfOptions() {
|
||||
return new Promise((resolve) => {
|
||||
let index = 0;
|
||||
(function next(index) {
|
||||
if (index > 10) {
|
||||
resolve([]);
|
||||
}
|
||||
let arr = dictOptions.value;
|
||||
if (arr && arr.length > 0) {
|
||||
resolve(arr);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
next(index++);
|
||||
}, 300);
|
||||
}
|
||||
})(index);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 值改变后 需要往外抛事件 触发下级节点的选项改变
|
||||
*/
|
||||
function valueChangeThenEmitNext(value, arr: any = []) {
|
||||
if (value && value.length > 0) {
|
||||
if (!arr || arr.length == 0) {
|
||||
arr = dictOptions.value;
|
||||
}
|
||||
let selected = arr.filter((item) => item.store === value);
|
||||
if (selected && selected.length > 0) {
|
||||
let id = selected[0].id;
|
||||
emit('next', id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉框筛选
|
||||
* @param input
|
||||
* @param option
|
||||
*/
|
||||
const filterOption = (input: string, option: any) => {
|
||||
let labelIf = option.children()[0]?.children.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
if (labelIf) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return {
|
||||
selectedValue,
|
||||
dictOptions,
|
||||
handleChange,
|
||||
filterOption,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<BasicForm ref="onlineFormRef" @register="registerForm" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { computed, defineComponent, ref, unref, watch, nextTick, toRaw } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { SUBMIT_FLOW_ID, SUBMIT_FLOW_KEY, VALIDATE_FAILED } from '../../types/onlineRender';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { pick } from 'lodash-es';
|
||||
import { useFormItems, getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import { Loading } from '/@/components/Loading';
|
||||
import { loadFormFieldsDefVal } from '../../util/FieldDefVal';
|
||||
|
||||
const urlObject = {
|
||||
optPre: '/online/cgform/api/form/',
|
||||
urlButtonAction: '/online/cgform/api/doButton',
|
||||
};
|
||||
const baseUrl = '/online/cgform/api/subform';
|
||||
export default {
|
||||
name: 'OnlineSubForm',
|
||||
components: {
|
||||
BasicForm,
|
||||
Loading,
|
||||
},
|
||||
props: {
|
||||
properties: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
mainId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
table: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
formTemplate: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
requiredFields: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
isUpdate: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
emits: ['formChange'],
|
||||
setup(props, { emit }) {
|
||||
console.log('进入online子表表单页面》》》》' + props.table);
|
||||
|
||||
// 表单ref
|
||||
const onlineFormRef = ref(null);
|
||||
// 表单是否渲染完成
|
||||
const formRendered = ref(false);
|
||||
const { createMessage: $message } = useMessage();
|
||||
const {
|
||||
formSchemas,
|
||||
defaultValueFields,
|
||||
changeDataIfArray2String,
|
||||
tableName,
|
||||
dbData,
|
||||
checkOnlyFieldValue,
|
||||
fieldDisplayStatus,
|
||||
createFormSchemas,
|
||||
baseColProps,
|
||||
labelCol,
|
||||
wrapperCol,
|
||||
labelWidth,
|
||||
} = useFormItems(props, onlineFormRef);
|
||||
//表单配置
|
||||
const [registerForm, { setProps, validate, resetFields, setFieldsValue, getFieldsValue, updateSchema, scrollToField }] = useForm({
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: baseColProps,
|
||||
// update-begin--author:liaozhiyang---date:20240429---for:【QQYUN-7632】 label栅格改成labelwidth固宽
|
||||
labelWidth,
|
||||
// update-end--author:liaozhiyang---date:20240429---for:【QQYUN-7632】 label栅格改成labelwidth固宽
|
||||
// update-begin--author:liaozhiyang---date:20240105---for:【QQYUN-7499】多列风格富文本、markdown增加独占一行功能
|
||||
labelCol,
|
||||
wrapperCol
|
||||
// update-end--author:liaozhiyang---date:20240105---for:【QQYUN-7499】多列风格富文本、markdown增加独占一行功能
|
||||
});
|
||||
const getFormItem = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
defHttp.get({ url: `online/cgform/api/getFormItem/${props.id}` }, { isTransformResponse: false }).then((res) => {
|
||||
resolve(res.result);
|
||||
});
|
||||
});
|
||||
}
|
||||
let extConfigJson;
|
||||
watch(
|
||||
() => props.table,
|
||||
() => {
|
||||
tableName.value = props.table;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
//监听配置改变事件
|
||||
watch(
|
||||
() => props.properties,
|
||||
async (valueObj) => {
|
||||
//重新渲染表单
|
||||
console.log('主表properties改变', props.properties);
|
||||
formRendered.value = false;
|
||||
addFormChangeEvent();
|
||||
// update-begin--author:liaozhiyang---date:20260318---for:【issues/9414】一对一子表设置label长度不生效
|
||||
if (!extConfigJson) {
|
||||
try {
|
||||
const data = await getFormItem();
|
||||
extConfigJson = JSON.parse(data.head.extConfigJson);
|
||||
extConfigJson = {
|
||||
formLabelLength: extConfigJson.formLabelLength,
|
||||
formLabelLengthShow: extConfigJson.formLabelLengthShow,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260318---for:【issues/9414】一对一子表设置label长度不生效
|
||||
createFormSchemas(props.properties, props.requiredFields, checkOnlyFieldValue, extConfigJson);
|
||||
formRendered.value = true;
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
//监听主表数据ID
|
||||
watch(
|
||||
() => props.mainId,
|
||||
(valueObj) => {
|
||||
//重新加载子表数据
|
||||
console.log('主表ID改变', props.mainId);
|
||||
// 此处延迟100毫秒是为了让properties的监听先执行
|
||||
setTimeout(() => {
|
||||
resetSubForm();
|
||||
}, 100);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.disabled,
|
||||
(val) => {
|
||||
setProps({ disabled: val });
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 监听表单改变事件
|
||||
*/
|
||||
async function addFormChangeEvent() {
|
||||
let formRefObject = await getRefPromise(onlineFormRef);
|
||||
formRefObject.$formValueChange = (field, value, changeFormData) => {
|
||||
let emitArgument = { [field]: value };
|
||||
// update-begin--author:liaozhiyang---date:20260317---for:【QQYUN-9441】online一对多加上关联记录和他表字段
|
||||
// 一对一子表 关联记录和他表字段
|
||||
if(changeFormData){
|
||||
setFieldsValue(changeFormData);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260317---for:【QQYUN-9441】online一对多加上关联记录和他表字段
|
||||
emit('formChange', emitArgument);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前表单默认值逻辑-进入新增页面触发
|
||||
*/
|
||||
function handleDefaultValue() {
|
||||
if (unref(props.isUpdate) === false) {
|
||||
let fieldProperties = toRaw(defaultValueFields[tableName.value]);
|
||||
loadFormFieldsDefVal(fieldProperties, (values) => {
|
||||
setFieldsValue(values);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当主表数据ID发生改变,子表重现获取数据
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function resetSubForm() {
|
||||
//TODO 填值规则
|
||||
// update-begin--author:sunjianlei --- date:20191111 --- for: 每次加载数据的时候都重新执行一遍填值规则 -----------
|
||||
//this.$emit('executeFillRule', {form:this.form, target: this})
|
||||
// update-end--author:sunjianlei --- date:20191111 --- for: 每次加载数据的时候都重新执行一遍填值规则 -----------
|
||||
await getRefPromise(formRendered);
|
||||
await resetFields();
|
||||
handleDefaultValue();
|
||||
const { table, mainId } = props;
|
||||
if (!table || !mainId) {
|
||||
return;
|
||||
}
|
||||
let values = await loadData(table, mainId);
|
||||
dbData.value = values;
|
||||
// VUEN-1033
|
||||
await setFieldsValue(values);
|
||||
}
|
||||
|
||||
function loadData(table, mainId) {
|
||||
let url = `${baseUrl}/${table}/${mainId}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
defHttp.get({ url }, { isTransformResponse: false }).then((res) => {
|
||||
console.log(res);
|
||||
if (res.success) {
|
||||
resolve(res.result);
|
||||
} else {
|
||||
console.log(res.message);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}).finally(() => {
|
||||
//resetFields()
|
||||
dbData.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
function getAll() {
|
||||
return new Promise((resolve, reject) => {
|
||||
validate()
|
||||
.then(() => {
|
||||
let formData = getFieldsValue();
|
||||
formData = changeDataIfArray2String(formData);
|
||||
resolve(formData);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.errorFields) {
|
||||
e.scrollToField = () => e.errorFields[0] && scrollToField(e.errorFields[0].name, { behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//获取表单事件对象 监听表单改变用到
|
||||
function getFormEvent() {
|
||||
let row = getFieldsValue();
|
||||
if (!row.id) {
|
||||
row.id = 'sub-change-temp-id';
|
||||
}
|
||||
return {
|
||||
row,
|
||||
target: context,
|
||||
};
|
||||
}
|
||||
|
||||
//设置表单的值
|
||||
function setValues(values) {
|
||||
setFieldsValue(values);
|
||||
}
|
||||
|
||||
function executeFillRule() {
|
||||
let formData = getFieldsValue();
|
||||
let fieldProperties = toRaw(defaultValueFields[tableName.value]);
|
||||
loadFormFieldsDefVal(fieldProperties, (values) => {
|
||||
setFieldsValue(values);
|
||||
}, formData);
|
||||
}
|
||||
|
||||
const context = {
|
||||
onlineFormRef,
|
||||
baseColProps,
|
||||
formSchemas,
|
||||
registerForm,
|
||||
setFieldsValue,
|
||||
getFieldsValue,
|
||||
getFormEvent,
|
||||
setValues,
|
||||
getAll,
|
||||
executeFillRule,
|
||||
sh: fieldDisplayStatus,
|
||||
resetFields,
|
||||
updateSchema,
|
||||
};
|
||||
return context;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// update-begin--author:liaozhiyang---date:20240527---for:【TV360X-263】tab风格一对一子表上传组件有数据没渲染出来
|
||||
:deep(.ant-upload-list-item-container) {
|
||||
&.ant-motion-collapse {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240527---for:【TV360X-263】tab风格一对一子表上传组件有数据没渲染出来
|
||||
</style>
|
||||
@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<detail-form :schemas="detailFormSchemas" :data="subFormData" :span="formSpan"></detail-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { ref, watch } from 'vue';
|
||||
import { BasicForm } from '/@/components/Form/index';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import { Loading } from '/@/components/Loading';
|
||||
import DetailForm from '../../extend/form/DetailForm.vue';
|
||||
import { getDetailFormSchemas } from '../../hooks/auto/useAutoForm';
|
||||
|
||||
const baseUrl = '/online/cgform/api/subform';
|
||||
export default {
|
||||
name: 'OnlineSubFormDetail',
|
||||
components: {
|
||||
BasicForm,
|
||||
Loading,
|
||||
DetailForm,
|
||||
},
|
||||
props: {
|
||||
properties: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
mainId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
table: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
formTemplate: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
emits: ['formChange'],
|
||||
setup(props) {
|
||||
// 表单是否渲染完成
|
||||
const formRendered = ref(false);
|
||||
const { createMessage: $message } = useMessage();
|
||||
const tableName = ref('');
|
||||
const subFormData = ref<any>({});
|
||||
const { detailFormSchemas, createFormSchemas, formSpan } = getDetailFormSchemas(props);
|
||||
|
||||
watch(
|
||||
() => props.table,
|
||||
() => {
|
||||
tableName.value = props.table;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
//监听配置改变事件
|
||||
watch(
|
||||
() => props.properties,
|
||||
() => {
|
||||
//重新渲染表单
|
||||
console.log('主表properties改变', props.properties);
|
||||
formRendered.value = false;
|
||||
createFormSchemas(props.properties);
|
||||
formRendered.value = true;
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
//监听主表数据ID
|
||||
watch(
|
||||
() => props.mainId,
|
||||
() => {
|
||||
//重新加载子表数据
|
||||
console.log('主表ID改变', props.mainId);
|
||||
// 此处延迟100毫秒是为了让properties的监听先执行
|
||||
setTimeout(() => {
|
||||
resetSubForm();
|
||||
}, 100);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**
|
||||
* 当主表数据ID发生改变,子表重现获取数据
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function resetSubForm() {
|
||||
await getRefPromise(formRendered);
|
||||
subFormData.value = {};
|
||||
const { table, mainId } = props;
|
||||
if (!table || !mainId) {
|
||||
return;
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20260413---for:【QQYUN-14951】一对一他表字段详情没值
|
||||
let data: any = (await loadData(table, mainId)) || {};
|
||||
await fillLinkTableFields(data);
|
||||
subFormData.value = data;
|
||||
// update-end--author:liaozhiyang---date:20260413---for:【QQYUN-14951】一对一他表字段详情没值
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20260413---for:【QQYUN-14951】一对一他表字段详情没值
|
||||
/**
|
||||
* 详情页一对一子表中,根据关联记录字段(link_table)的值查询关联数据,自动填充他表字段(link_table_field)的值
|
||||
*/
|
||||
async function fillLinkTableFields(data) {
|
||||
const schemas = detailFormSchemas.value;
|
||||
for (const schema of schemas) {
|
||||
if (schema.view === 'link_table' && (schema as any).linkFields?.length > 0) {
|
||||
const fieldValue = data[schema.field];
|
||||
if (fieldValue) {
|
||||
const valueField = (schema as any).dictCode || 'id';
|
||||
const vals = String(fieldValue).split(',');
|
||||
const params = {
|
||||
pageSize: vals.length,
|
||||
pageNo: 1,
|
||||
superQueryMatchType: 'and',
|
||||
superQueryParams: encodeURI(JSON.stringify([{ field: valueField, rule: 'in', val: fieldValue }])),
|
||||
};
|
||||
try {
|
||||
const result = await defHttp.get({ url: '/online/cgform/api/getData/' + (schema as any).dictTable, params });
|
||||
const records = result?.records || [];
|
||||
for (const linkField of (schema as any).linkFields) {
|
||||
const [formField, tableField] = linkField.split(',');
|
||||
if (records.length > 0) {
|
||||
data[formField] = records.map((r: any) => r[tableField] ?? '').join(',');
|
||||
} else {
|
||||
data[formField] = '';
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('填充他表字段失败:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260413---for:【QQYUN-14951】一对一他表字段详情没值
|
||||
|
||||
|
||||
async function loadData(table, mainId) {
|
||||
let url = `${baseUrl}/${table}/${mainId}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
defHttp.get({ url }, { isTransformResponse: false }).then((res) => {
|
||||
console.log(res);
|
||||
if (res.success) {
|
||||
resolve(res.result);
|
||||
} else {
|
||||
reject(res.message);
|
||||
}
|
||||
});
|
||||
}).catch((e) => {
|
||||
console.warn('子表获取数据失败:', e);
|
||||
return Promise.resolve({});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
detailFormSchemas,
|
||||
subFormData,
|
||||
formSpan,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div class="cust-onl-form">
|
||||
<a-spin :spinning="spinLoading">
|
||||
<online-form
|
||||
ref="onlineFormCompRef"
|
||||
:id="formId"
|
||||
:disabled="disabled"
|
||||
:form-template="formTemplate"
|
||||
:isTree="isTreeForm"
|
||||
:pidField="pidFieldName"
|
||||
:taskId="taskId"
|
||||
@rendered="renderSuccess"
|
||||
@success="handleSuccess"
|
||||
@validate="validateBack"
|
||||
@close="handleClose"
|
||||
>
|
||||
<template #bottom>
|
||||
<div style="width: 100%; text-align: center; margin-top: 5px" v-if="!disabled && !spinLoading && showSubmitButton">
|
||||
<a-button preIcon="ant-design:check" style="width: 126px" type="primary" @click="handleSubmit" :loading="buttonLoading"> 提 交 </a-button>
|
||||
</div>
|
||||
<!-- 任务办理意见 -->
|
||||
<TaskOpinionList class="task-opinion" :taskId="taskOriginalId" :procInsId="procInsId" :processTabType="processTabType" />
|
||||
</template>
|
||||
</online-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 工作流online表单调--中转的意义在于在此查询表单信息
|
||||
*/
|
||||
import OnlineForm from './OnlineForm.vue';
|
||||
import { defineComponent, ref, watch, nextTick, computed } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import TaskOpinionList from "@/views/super/online/cgform/auto/comp/TaskOpinionList.vue";
|
||||
import {useGlobSetting} from "@/hooks/setting";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ProcessOnlineForm',
|
||||
inheritAttrs: false,
|
||||
components: {
|
||||
TaskOpinionList,
|
||||
OnlineForm,
|
||||
},
|
||||
props: {
|
||||
dataId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
tableName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
taskOriginalId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
procInsId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
processTabType: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['success','validate'],
|
||||
setup(props, { emit }) {
|
||||
const onlineFormCompRef = ref();
|
||||
const formId = ref('');
|
||||
const formTemplate = ref(1);
|
||||
const isTreeForm = ref(false);
|
||||
const pidFieldName = ref('');
|
||||
const spinLoading = ref(false);
|
||||
|
||||
//监听表名改变 重新加载表单
|
||||
watch(
|
||||
() => props.tableName,
|
||||
(val) => {
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
loadFormItems();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
//update-begin-author:liusq---date:2026-01-20--for: 增加判断,新弹窗online表单不显示提交按钮
|
||||
const showSubmitButton = computed(() => {
|
||||
return !useGlobSetting().useNewTaskModal;
|
||||
});
|
||||
//update-end-author:liusq---date:2026-01-20--for: 增加判断,新弹窗online表单不显示提交按钮
|
||||
//加载表单
|
||||
async function loadFormItems() {
|
||||
spinLoading.value = true;
|
||||
const url = `/online/cgform/api/getFormItemBytbname/${props.tableName}`;
|
||||
const params = { taskId: props.taskId };
|
||||
try {
|
||||
let result = await defHttp.get({ url, params });
|
||||
console.log('动态表单查询结果是:', result);
|
||||
formId.value = result.head.id;
|
||||
formTemplate.value = Number(result.head.formTemplate || 1);
|
||||
isTreeForm.value = result.head.isTree === 'Y';
|
||||
pidFieldName.value = result.head.treeParentIdField || '';
|
||||
await nextTick(async () => {
|
||||
let myForm = await getRefPromise(onlineFormCompRef);
|
||||
myForm.createRootProperties(result);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('流程表单查询异常', e);
|
||||
}
|
||||
}
|
||||
|
||||
//渲染完成 装载数据
|
||||
async function renderSuccess() {
|
||||
let myForm = await getRefPromise(onlineFormCompRef);
|
||||
spinLoading.value = false;
|
||||
myForm.show(true, {
|
||||
id: props.dataId,
|
||||
});
|
||||
}
|
||||
|
||||
//表单提交
|
||||
const buttonLoading = ref(false);
|
||||
async function handleSubmit(showTip = true) {
|
||||
buttonLoading.value = true;
|
||||
onlineFormCompRef.value.handleSubmit(showTip);
|
||||
}
|
||||
function handleSuccess(data = null) {
|
||||
buttonLoading.value = false;
|
||||
emit('success', data);
|
||||
}
|
||||
//表单校验
|
||||
function handleValidate() {
|
||||
onlineFormCompRef.value.handleValidate();
|
||||
}
|
||||
//表单校验失败
|
||||
function validateBack(data = null) {
|
||||
emit('validate', data);
|
||||
}
|
||||
function handleClose() {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
|
||||
return {
|
||||
onlineFormCompRef,
|
||||
formId,
|
||||
formTemplate,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
renderSuccess,
|
||||
handleSuccess,
|
||||
handleClose,
|
||||
handleSubmit,
|
||||
handleValidate,
|
||||
validateBack,
|
||||
buttonLoading,
|
||||
spinLoading,
|
||||
showSubmitButton,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cust-onl-form .ant-input-disabled {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.cust-onl-form .ant-select-disabled .ant-select-selection {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.cust-onl-form .ant-input-number-disabled {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<!-- 意见区域 -->
|
||||
<div class="opinion-area" v-if="opinionPrint">
|
||||
<!-- 标题头 -->
|
||||
<div class="opinion-header">
|
||||
<h3>审批意见</h3>
|
||||
<div class="header-line"></div>
|
||||
</div>
|
||||
|
||||
<!-- 意见列表 -->
|
||||
<div class="opinion-list">
|
||||
<a-list itemLayout="vertical" :split="false">
|
||||
<template v-for="(item, index) in bpmLogList" :key="index">
|
||||
<a-list-item class="opinion-item">
|
||||
<a-list-item-meta :description="item.remarks">
|
||||
<template #title>
|
||||
<div class="opinion-meta">
|
||||
<span class="user-name">{{ item.opUserName }}</span>
|
||||
<span class="task-tag">[{{ item.taskName }}]</span>
|
||||
<span class="op-time">{{ formatTime(item.opTime) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// 监听流程流转信息
|
||||
import {inject, ref, watchEffect} from 'vue';
|
||||
import { taskTransInfo } from '@/views/super/bpm/process/personalOffice/myHandleTask/task.handle.api';
|
||||
import dayjs from 'dayjs';
|
||||
// 参数
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
procInsId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
processTabType: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
});
|
||||
|
||||
const opinionPrint = inject('opinionPrint');
|
||||
// 审批记录/意见信息
|
||||
const bpmLogList = ref([]);
|
||||
|
||||
// 监听流程流转信息
|
||||
watchEffect(() => {
|
||||
props.procInsId && getTaskTransInfo();
|
||||
});
|
||||
// 获取流程流转信息
|
||||
async function getTaskTransInfo() {
|
||||
let taskType = props.processTabType || 'history';
|
||||
//查询条件-run只需要taskId, history只需要procInstId
|
||||
let params = { taskId: props.taskId, procInstId: props.procInsId };
|
||||
let data = await taskTransInfo(params, taskType);
|
||||
bpmLogList.value = data.bpmLogList;
|
||||
}
|
||||
// 时间格式化
|
||||
function formatTime(time) {
|
||||
if (!time) return '';
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
/* 意见区域整体样式 */
|
||||
.opinion-area {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 标题头样式 */
|
||||
.opinion-header {
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.opinion-header h3 {
|
||||
color: #333;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.header-line {
|
||||
height: 2px;
|
||||
background: linear-gradient(to right, #1890ff, transparent);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 意见列表样式 */
|
||||
.opinion-list {
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
/* 单个意见项样式 */
|
||||
.opinion-item {
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 12px;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.opinion-item:hover {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 用户信息样式 */
|
||||
.opinion-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.task-tag {
|
||||
color: #ff6d75;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.op-time {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 意见内容样式 */
|
||||
:deep(.ant-list-item-meta-description) {
|
||||
padding-left: 42px;
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body * {
|
||||
visibility: hidden;
|
||||
}
|
||||
.daily-opinion,
|
||||
.daily-opinion * {
|
||||
visibility: visible;
|
||||
}
|
||||
.daily-opinion {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
table {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
ul,
|
||||
ol {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,177 @@
|
||||
import InputWidget from './impl/InputWidget';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import DateWidget from './impl/DateWidget';
|
||||
import SelectWidget from './impl/SelectWidget';
|
||||
import PasswordWidget from './impl/PasswordWidget';
|
||||
import FileWidget from './impl/FileWidget';
|
||||
import ImageWidget from './impl/ImageWidget';
|
||||
import TextAreaWidget from './impl/TextAreaWidget';
|
||||
import SelectMultiWidget from './impl/SelectMultiWidget';
|
||||
import SelectSearchWidget from './impl/SelectSearchWidget';
|
||||
import PopupWidget from './impl/PopupWidget';
|
||||
// update-begin--author:liaozhiyang---date:20240130---for:【QQYUN-7961】popupDict字典
|
||||
import PopupDictWidget from './impl/PopupDictWidget';
|
||||
// update-end--author:liaozhiyang---date:20240130---for:【QQYUN-7961】popupDict字典
|
||||
import TreeCategoryWidget from './impl/TreeCategoryWidget';
|
||||
import SelectDepartWidget from './impl/SelectDepartWidget';
|
||||
import SelectUserWidget from './impl/SelectUserWidget';
|
||||
import EditorWidget from './impl/EditorWidget';
|
||||
import MarkdownWidget from './impl/MarkdownWidget';
|
||||
import PcaWidget from './impl/PcaWidget';
|
||||
import AreaLinkage from './impl/AreaLinkage';
|
||||
import TreeSelectWidget from './impl/TreeSelectWidget';
|
||||
import RadioWidget from './impl/RadioWidget';
|
||||
import CheckboxWidget from './impl/CheckboxWidget';
|
||||
import SwitchWidget from './impl/SwitchWidget';
|
||||
import TimeWidget from './impl/TimeWidget';
|
||||
import LinkDownWidget from './impl/LinkDownWidget';
|
||||
import SlotWidget from './impl/SlotWidget';
|
||||
import NumberWidget from './impl/NumberWidget';
|
||||
import LinkTableWidget from './impl/LinkTableWidget'
|
||||
import LinkTableFieldWidget from './impl/LinkTableFieldWidget'
|
||||
import LinkTableForQueryWidget from './impl/LinkTableForQueryWidget'
|
||||
import CascaderPcaForQueryWidget from './impl/CascaderPcaForQueryWidget'
|
||||
import SelectUser2Widget from './impl/SelectUser2Widget'
|
||||
import RangeWidget from "./impl/RangeWidget";
|
||||
|
||||
export default class FormSchemaFactory {
|
||||
static createFormSchema(key, data, queryItem) {
|
||||
let view = data.view;
|
||||
switch (view) {
|
||||
case 'password':
|
||||
//2.密码输入框
|
||||
return new PasswordWidget(key, data);
|
||||
case 'list':
|
||||
//3.下拉框
|
||||
return new SelectWidget(key, data);
|
||||
case 'radio':
|
||||
// 4. 单选
|
||||
return new RadioWidget(key, data);
|
||||
case 'checkbox':
|
||||
// 5.多选
|
||||
return new CheckboxWidget(key, data);
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
// 6.日期
|
||||
// 7.日期时间
|
||||
return new DateWidget(key, data, queryItem);
|
||||
case 'time':
|
||||
// 8 时间
|
||||
return new TimeWidget(key, data);
|
||||
case 'file':
|
||||
// 9.文件
|
||||
return new FileWidget(key, data);
|
||||
case 'image':
|
||||
// 10.图片
|
||||
return new ImageWidget(key, data);
|
||||
case 'textarea':
|
||||
// 11.多行文本
|
||||
return new TextAreaWidget(key, data);
|
||||
case 'list_multi':
|
||||
// 12.下拉多选框
|
||||
return new SelectMultiWidget(key, data);
|
||||
case 'sel_search':
|
||||
// 13.下拉搜索框
|
||||
return new SelectSearchWidget(key, data);
|
||||
case 'popup':
|
||||
// 14. popup
|
||||
return new PopupWidget(key, data);
|
||||
case 'cat_tree':
|
||||
// 15.分类字典树
|
||||
return new TreeCategoryWidget(key, data);
|
||||
case 'sel_depart':
|
||||
// 16.部门选择
|
||||
return new SelectDepartWidget(key, data);
|
||||
case 'sel_user':
|
||||
// 17.用户选择
|
||||
return new SelectUserWidget(key, data);
|
||||
case 'umeditor':
|
||||
// 18.富文本
|
||||
return new EditorWidget(key, data);
|
||||
case 'markdown':
|
||||
// 19.MarkDown
|
||||
return new MarkdownWidget(key, data);
|
||||
case 'pca':
|
||||
// 20.省市区
|
||||
// update-begin--author:liaozhiyang---date:20240607---for:【TV360X-501】省市区换新组件
|
||||
// return new PcaWidget(key, data);
|
||||
return new AreaLinkage(key, data);
|
||||
// update-end--author:liaozhiyang---date:20240607---for:【TV360X-501】省市区换新组件
|
||||
case 'link_down':
|
||||
// 21.联动组件
|
||||
return new LinkDownWidget(key, data);
|
||||
case 'sel_tree':
|
||||
// 22.自定义树控件
|
||||
return new TreeSelectWidget(key, data);
|
||||
case 'switch':
|
||||
// 23.开关组件
|
||||
return new SwitchWidget(key, data);
|
||||
case 'link_table':
|
||||
// 24.关联记录
|
||||
return new LinkTableWidget(key, data);
|
||||
case 'link_table_field':
|
||||
// 25.他表字段
|
||||
return new LinkTableFieldWidget(key, data);
|
||||
// update-begin--author:liaozhiyang---date:20240130---for:【QQYUN-7961】popupDict字典
|
||||
case 'popup_dict':
|
||||
// 14. popup字典
|
||||
return new PopupDictWidget(key, data);
|
||||
// update-end--author:liaozhiyang---date:20240130---for:【QQYUN-7961】popupDict字典
|
||||
case 'slot':
|
||||
// slot
|
||||
return new SlotWidget(key, data);
|
||||
case 'LinkTableForQuery':
|
||||
return new LinkTableForQueryWidget(key, data);
|
||||
case 'CascaderPcaForQuery':
|
||||
return new CascaderPcaForQueryWidget(key, data, queryItem);
|
||||
case 'select_user2':
|
||||
return new SelectUser2Widget(key, data);
|
||||
case 'rangeDate':
|
||||
case 'rangeTime':
|
||||
case 'rangeNumber':
|
||||
return new RangeWidget(key, data);
|
||||
case 'hidden':
|
||||
// 隐藏的控件 如分类树的文本
|
||||
return new InputWidget(key, data).isHidden();
|
||||
default:
|
||||
if (data.type == 'number') {
|
||||
return new NumberWidget(key, data);
|
||||
} else {
|
||||
//1.普通输入框
|
||||
return new InputWidget(key, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static createSlotFormSchema(key, data) {
|
||||
let slotFs = new SlotWidget(key, data);
|
||||
let view = data.view;
|
||||
if ('date' == view) {
|
||||
slotFs.groupDate();
|
||||
} else if ('datetime' == view) {
|
||||
slotFs.groupDatetime();
|
||||
} else if ('time' == view) {
|
||||
// update-begin--author:liaozhiyang---date:20240517---for:【QQYUN-9348】增加online查询区域时间范围查询功能
|
||||
slotFs.groupTime();
|
||||
// update-end--author:liaozhiyang---date:20240517---for:【QQYUN-9348】增加online查询区域时间范围查询功能
|
||||
} else {
|
||||
let type = data.type;
|
||||
if (type == 'number' || type == 'integer') {
|
||||
slotFs.groupNumber();
|
||||
}
|
||||
}
|
||||
return slotFs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单ID 默认是隐藏的
|
||||
*/
|
||||
static createIdField(): FormSchema {
|
||||
return {
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,469 @@
|
||||
import {computed, watch} from 'vue'
|
||||
import { FormSchema, Rule } from '/@/components/Form';
|
||||
import { FieldExtends, POP_CONTAINER } from '../../../types/onlineRender';
|
||||
import { LABELLENGTH } from '../../../util/constant';
|
||||
import {replaceUserInfoByExpression} from "@/utils/common/compUtils";
|
||||
/**
|
||||
* 1.部门选择/用户选择 无:单选配置
|
||||
* 控件类
|
||||
*/
|
||||
export default abstract class IFormSchema {
|
||||
_data;
|
||||
field: string;
|
||||
label: string;
|
||||
labelLength: number;
|
||||
formRef: any;
|
||||
hidden: boolean;
|
||||
order: number;
|
||||
required: boolean;
|
||||
onlyValidator: any;
|
||||
hasChange: boolean;
|
||||
pre: string;
|
||||
setFieldsValue: any;
|
||||
schemaProp: any;
|
||||
searchForm: boolean;
|
||||
disabled: boolean;
|
||||
popContainer: string;
|
||||
inPopover: boolean;
|
||||
|
||||
constructor(key, data) {
|
||||
// 考虑不需要存data
|
||||
this._data = data;
|
||||
this.field = key;
|
||||
this.label = data.title;
|
||||
this.hidden = false;
|
||||
this.order = data.order || 999;
|
||||
this.required = false;
|
||||
this.onlyValidator = '';
|
||||
this.setFieldsValue = '';
|
||||
this.hasChange = true;
|
||||
if (key.indexOf('@') > 0) {
|
||||
this.pre = key.substring(0, key.indexOf('@') + 1);
|
||||
} else {
|
||||
this.pre = '';
|
||||
}
|
||||
this.schemaProp = {};
|
||||
this.searchForm = false;
|
||||
this.disabled = false;
|
||||
this.popContainer = '';
|
||||
this.handleWidgetAttr(data);
|
||||
this.inPopover = false;
|
||||
this.labelLength = LABELLENGTH;
|
||||
this.initLabelLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最终的表单配置项,外面获取调用此方法
|
||||
*/
|
||||
getFormItemSchema(): FormSchema {
|
||||
let schema = this.getItem();
|
||||
this.addDefaultChangeEvent(schema);
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表单配置,子类重写此方法
|
||||
*/
|
||||
getItem(): FormSchema {
|
||||
let fs: FormSchema = {
|
||||
field: this.field,
|
||||
label: this.label,
|
||||
labelLength: this.labelLength,
|
||||
component: 'Input',
|
||||
itemProps:{
|
||||
labelCol:{
|
||||
class: 'online-form-label'
|
||||
}
|
||||
}
|
||||
};
|
||||
let rules = this.getRule();
|
||||
if (rules.length > 0 && this.onlyValidator) {
|
||||
fs['rules'] = rules;
|
||||
}
|
||||
if (this.hidden === true) {
|
||||
fs['show'] = false;
|
||||
}
|
||||
return fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表单ref
|
||||
* popup、分类树需要关联设置其他表单值的时候用到
|
||||
* @param ref
|
||||
*/
|
||||
setFormRef(ref) {
|
||||
this.formRef = ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表单元素隐藏
|
||||
*/
|
||||
isHidden() {
|
||||
this.hidden = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否必填项
|
||||
* @param array
|
||||
*/
|
||||
isRequired(array) {
|
||||
// 子表必填 TODO
|
||||
if (array && array.length > 0) {
|
||||
if (array.indexOf(this.field) >= 0) {
|
||||
this.required = true;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 label长度
|
||||
*/
|
||||
initLabelLength(){
|
||||
let obj = this.getExtendData()
|
||||
if(obj && obj.labelLength){
|
||||
this.labelLength = obj.labelLength;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展参数
|
||||
*/
|
||||
getExtendData() {
|
||||
let extend: FieldExtends = {};
|
||||
let { fieldExtendJson } = this._data;
|
||||
if (fieldExtendJson) {
|
||||
if (typeof fieldExtendJson == 'string') {
|
||||
try {
|
||||
let json = JSON.parse(fieldExtendJson);
|
||||
extend = { ...json };
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return extend;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取和此字段相关的其他字段 需要设置其为隐藏
|
||||
*/
|
||||
getRelatedHideFields(): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* placeholder
|
||||
*/
|
||||
getPlaceholder(view) {
|
||||
let text = '请输入';
|
||||
// update-begin--author:liaozhiyang---date:20240521---for:【TV360X-218】针对组件分别提示对应的校验语
|
||||
if (
|
||||
[
|
||||
'list',
|
||||
'radio',
|
||||
'checkbox',
|
||||
'date',
|
||||
'datetime',
|
||||
'time',
|
||||
'list_multi',
|
||||
'sel_search',
|
||||
'popup',
|
||||
'cat_tree',
|
||||
'sel_depart',
|
||||
'sel_user',
|
||||
'pca',
|
||||
'link_down',
|
||||
'sel_tree',
|
||||
'switch',
|
||||
'link_table',
|
||||
'link_table_field',
|
||||
'popup_dict',
|
||||
'LinkTableForQuery',
|
||||
'CascaderPcaForQuery',
|
||||
'select_user2',
|
||||
'rangeDate',
|
||||
'rangeTime',
|
||||
'rangeNumber',
|
||||
].includes(view)
|
||||
) {
|
||||
text = '请选择';
|
||||
} else if (['file', 'image'].includes(view)) {
|
||||
text = '请上传';
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240521---for:【TV360X-218】针对组件分别提示对应的校验语
|
||||
return text + this.label;
|
||||
}
|
||||
|
||||
/**
|
||||
* 唯一校验
|
||||
*/
|
||||
setOnlyValidateFun(validateFun) {
|
||||
if (validateFun) {
|
||||
this.onlyValidator = async (rule, value) => {
|
||||
let error = await validateFun(rule, value);
|
||||
if (!error) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取校验规则
|
||||
*/
|
||||
getRule(): any[] {
|
||||
let rules: Rule[] = [];
|
||||
const { view, errorInfo, pattern, type, fieldExtendJson } = this._data;
|
||||
if (this.required === true) {
|
||||
let msg = this.getPlaceholder(view);
|
||||
// update-begin--author:liaozhiyang---date:20240520---for:【TV360X-80】扩展参数配置中的校验提示不生效
|
||||
if (fieldExtendJson) {
|
||||
const json = JSON.parse(fieldExtendJson);
|
||||
if (json.validateError) {
|
||||
msg = json.validateError;
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240520---for:【TV360X-80】扩展参数配置中的校验提示不生效
|
||||
if (errorInfo) {
|
||||
msg = errorInfo;
|
||||
}
|
||||
if (view == 'sel_depart' || view == 'sel_user') {
|
||||
//如果是部门和用户组件 使用 required:true
|
||||
this.schemaProp['required'] = true;
|
||||
// update-begin--author:liaozhiyang---date:20240429---for:【QQYUN-9109】online使用部门和用户组件必填时label前面没有必填的*号
|
||||
rules.push({ required: true, message: msg });
|
||||
// update-end--author:liaozhiyang---date:20240429---for:【QQYUN-9109】online使用部门和用户组件必填时label前面没有必填的*号
|
||||
} else {
|
||||
rules.push({ required: true, message: msg });
|
||||
}
|
||||
}
|
||||
if ('sel_user' == view) {
|
||||
if (pattern === 'only' && this.onlyValidator) {
|
||||
rules.push({ validator: this.onlyValidator });
|
||||
}
|
||||
}
|
||||
if ('list' === view || 'radio' === view || 'markdown' === view || 'pca' === view || view.indexOf('sel') >= 0 || 'time' === view) {
|
||||
return rules;
|
||||
}
|
||||
if (view.indexOf('upload') >= 0 || view.indexOf('file') >= 0 || view.indexOf('image') >= 0) {
|
||||
return rules;
|
||||
}
|
||||
if (pattern) {
|
||||
if (pattern === 'only') {
|
||||
if (this.onlyValidator) {
|
||||
rules.push({ validator: this.onlyValidator });
|
||||
}
|
||||
} else if (pattern === 'z') {
|
||||
if (type == 'number' || type == 'integer') {
|
||||
// this.onlyInteger=true TODO
|
||||
} else {
|
||||
rules.push({ pattern: /^-?\d+$/, message: '请输入整数' });
|
||||
}
|
||||
} else {
|
||||
let msg = errorInfo || '正则校验失败';
|
||||
let reg
|
||||
try {
|
||||
reg = new RegExp(pattern);
|
||||
if (!reg) {
|
||||
reg = pattern;
|
||||
}
|
||||
} catch {
|
||||
reg = pattern;
|
||||
}
|
||||
rules.push({ pattern: reg, message: msg });
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加默认的change事件
|
||||
* @param schema
|
||||
*/
|
||||
addDefaultChangeEvent(schema) {
|
||||
if (this.hasChange) {
|
||||
if (!schema.componentProps) {
|
||||
schema.componentProps = {};
|
||||
}
|
||||
//update-begin-author:taoyan date:2022-5-24 for: VUEN-1095 只读未控制住
|
||||
if (this.disabled == true) {
|
||||
schema.componentProps.disabled = true;
|
||||
}
|
||||
//update-end-author:taoyan date:2022-5-24 for: VUEN-1095 只读未控制住
|
||||
if (!schema.componentProps.hasOwnProperty('onChange')) {
|
||||
schema.componentProps['onChange'] = (value, formData) => {
|
||||
if (value instanceof Event) {
|
||||
// 输入框 value是event对象
|
||||
value = (value.target as any).value;
|
||||
}
|
||||
// 部门组件抛出事件的value是数组
|
||||
if (value instanceof Array) {
|
||||
value = value.join(',');
|
||||
}
|
||||
// VUEN-1467【vue3 工作流】流程处理 一对多表单 子表tab切换后,关闭不了 导致整个浏览器无法操作 多操作几次,不一定每次必现---
|
||||
if(!this.formRef || !this.formRef.value || !this.formRef.value.$formValueChange){
|
||||
console.log('当前表单无法触发change事件,field:'+this.field)
|
||||
}else{
|
||||
this.formRef.value.$formValueChange(this.field, value, formData)
|
||||
}
|
||||
};
|
||||
// update-begin--author:liaozhiyang---date:20251011---for:【issues/8791】js增强popup弹框的onlChange()没生效
|
||||
if (schema.component === 'JPopup') {
|
||||
schema.componentProps['onPopUpChange'] = schema.componentProps['onChange']
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20251011---for:【issues/8791】js增强popup弹框的onlChange()没生效
|
||||
}
|
||||
}
|
||||
// 顺带处理其他的 schemaProp
|
||||
Object.keys(this.schemaProp).map((k) => {
|
||||
schema[k] = this.schemaProp[k];
|
||||
});
|
||||
}
|
||||
|
||||
noChange() {
|
||||
this.hasChange = false;
|
||||
}
|
||||
|
||||
updateField(field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* 高级查询 没有表单ref对象 手动设置setFieldValue方法用于 popup设置表单值
|
||||
*/
|
||||
setFunctionForFieldValue(func) {
|
||||
if (func) {
|
||||
this.setFieldsValue = func;
|
||||
}
|
||||
}
|
||||
|
||||
asSearchForm() {
|
||||
this.searchForm = true;
|
||||
}
|
||||
|
||||
/**获取modal作为类下拉组件pop的父容器*/
|
||||
getModalAsContainer() {
|
||||
let ele = this.getPopContainer();
|
||||
// update-begin--author:liaozhiyang---date:20231205---for:【QQYUN-7150】online缓存路由打开多页导致下拉类型的组件打不开
|
||||
if (ele != 'body') {
|
||||
const elems = document.querySelectorAll(ele);
|
||||
if (elems && elems.length > 1) {
|
||||
const data: HTMLElement[] = [];
|
||||
elems.forEach((item: HTMLElement) => {
|
||||
if (!(item.offsetWidth == 0 && item.offsetHeight == 0)) {
|
||||
data.push(item);
|
||||
}
|
||||
});
|
||||
if (data.length === 1) {
|
||||
return data[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20231205---for:【QQYUN-7150】online缓存路由打开多页导致下拉类型的组件打不开
|
||||
return document.querySelector(ele);
|
||||
}
|
||||
|
||||
/**区分modal表单和查询表单*/
|
||||
getPopContainer() {
|
||||
if (this.searchForm === true) {
|
||||
return 'body';
|
||||
} else if(this.inPopover === true){
|
||||
return `.${this.popContainer}`;
|
||||
}else if(this.popContainer){
|
||||
return `.${this.popContainer} .ant-modal-content`
|
||||
}else {
|
||||
return POP_CONTAINER;
|
||||
}
|
||||
}
|
||||
|
||||
handleWidgetAttr(data) {
|
||||
if (data.ui) {
|
||||
if (data.ui.widgetattrs) {
|
||||
if (data.ui.widgetattrs.disabled == true) {
|
||||
this.disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 popContainer
|
||||
*/
|
||||
setCustomPopContainer(modalClass){
|
||||
this.popContainer = modalClass;
|
||||
}
|
||||
|
||||
//update-begin-author:taoyan date:2022-8-5 for: 他表字段/关联记录用
|
||||
// 获取他表字段的 配置信息
|
||||
getLinkFieldInfo():any{
|
||||
return '';
|
||||
}
|
||||
|
||||
// 1.将他表字段的配置信息设置到关联记录字段上
|
||||
setOtherInfo(_arg){
|
||||
}
|
||||
//update-end-author:taoyan date:2022-8-5 for: 他表字段/关联记录用
|
||||
|
||||
// 表单设计器高级查询用
|
||||
isInPopover(){
|
||||
this.inPopover = true;
|
||||
}
|
||||
|
||||
handleDictTableParams() {
|
||||
if (!this.formRef.value) {
|
||||
return
|
||||
}
|
||||
const dictTable = this._data.dictTable as string
|
||||
if (!dictTable) {
|
||||
return
|
||||
}
|
||||
const matches = dictTable.match(/\${([^}]+)}/g)
|
||||
if (!matches || matches.length == 0) {
|
||||
return
|
||||
}
|
||||
// 去除 ${}
|
||||
const keys = matches.map((item: string) => item.replace('${', '').replace('}', ''))
|
||||
const values = computed(() => {
|
||||
const formModel = this.formRef.value.formModel
|
||||
return keys.map((key) => formModel[key]).join('');
|
||||
})
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
watch(values, () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
const formModel = this.formRef.value.formModel
|
||||
// 替换动态参数,如果有 ${xxx} 则替换为实际值
|
||||
let tempDictTable = dictTable.replace(/\${([^}]+)}/g, (_$0, $1) => {
|
||||
if (formModel[$1] == null) {
|
||||
return ''
|
||||
}
|
||||
return formModel[$1]
|
||||
});
|
||||
this.updateDictTable(tempDictTable)
|
||||
}, 150)
|
||||
}, {immediate: true})
|
||||
}
|
||||
|
||||
updateDictTable(_dictTable: string) {
|
||||
console.log('请在子类实现 updateDictTable 方法')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表字典的编码,可替换系统变量
|
||||
* @param dictTable
|
||||
* @param dictText
|
||||
* @param dictCode
|
||||
*/
|
||||
genDictTableCode(dictTable: string, dictText: string, dictCode: string) {
|
||||
// 替换系统变量
|
||||
dictTable = replaceUserInfoByExpression(dictTable)
|
||||
return encodeURI(`${dictTable},${dictText},${dictCode}`);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
/**
|
||||
* 省市区
|
||||
*/
|
||||
export default class PcaWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
// update-begin--author:liaozhiyang---date:20260204---for:【QQYUN-14694】online支持配置独立的省、市、县
|
||||
const extendData: any = this.getExtendData();
|
||||
const componentProps: any = {}
|
||||
if (extendData.displayLevel) {
|
||||
componentProps.displayLevel = extendData.displayLevel;
|
||||
componentProps.saveCode = extendData.displayLevel === 'all' ? 'region' : componentProps.displayLevel;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260204---for:【QQYUN-14694】online支持配置独立的省、市、县
|
||||
return Object.assign({}, item, {
|
||||
component: 'JAreaLinkage',
|
||||
componentProps: {
|
||||
saveCode: 'region',
|
||||
...componentProps,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 表单设计器-省市区查询
|
||||
*/
|
||||
export default class CascaderPcaForQueryWidget extends IFormSchema {
|
||||
|
||||
schema: Recordable;
|
||||
// 省市县联动级别
|
||||
areaLevel: number;
|
||||
// 是否允许更改级别
|
||||
allowChangeLevel: boolean;
|
||||
|
||||
constructor(key: string, data: Recordable, queryItem: Recordable) {
|
||||
super(key, data);
|
||||
this.schema = data
|
||||
this.areaLevel = data['areaLevel'] ?? 3;
|
||||
// 只有等于和不等于才能更改级别
|
||||
this.allowChangeLevel = ['eq', 'ne'].includes(queryItem?.rule)
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'CascaderPcaInFilter',
|
||||
componentProps:{
|
||||
areaLevel: this.areaLevel,
|
||||
allowChangeLevel: this.allowChangeLevel,
|
||||
placeholder: '请选择…',
|
||||
style: {
|
||||
width: '100%',
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* checkbox
|
||||
*/
|
||||
export default class CheckboxWidget extends IFormSchema {
|
||||
/*title-value*/
|
||||
options: any[];
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.options = this.getOptions(data['enum']);
|
||||
}
|
||||
|
||||
setFormRef(ref) {
|
||||
super.setFormRef(ref);
|
||||
this.handleDictTableParams();
|
||||
}
|
||||
|
||||
updateDictTable(dictTable: string) {
|
||||
this.formRef.value.updateSchema(({
|
||||
field: this.field,
|
||||
componentProps: {
|
||||
options:[],
|
||||
dictCode: this.genDictTableCode(dictTable, this._data.dictText, this._data.dictCode),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JCheckbox',
|
||||
componentProps: {
|
||||
options: this.options,
|
||||
triggerChange: true,
|
||||
// update-begin--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
useDicColor: true,
|
||||
// update-end--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getOptions(array) {
|
||||
if (!array || array.length == 0) {
|
||||
return [];
|
||||
}
|
||||
let arr: any[] = [];
|
||||
for (let item of array) {
|
||||
arr.push({
|
||||
value: item.value,
|
||||
label: item.title,
|
||||
// update-begin--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
color: item.color,
|
||||
// update-end--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
enum DateFormat {
|
||||
datetime = 'YYYY-MM-DD HH:mm:ss',
|
||||
date = 'YYYY-MM-DD',
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期、时间
|
||||
*/
|
||||
export default class DateWidget extends IFormSchema {
|
||||
format: string;
|
||||
showTime: boolean;
|
||||
picker: string | undefined;
|
||||
|
||||
allowSelectRange: boolean;
|
||||
|
||||
constructor(key, data, queryItem) {
|
||||
super(key, data);
|
||||
this.format = DateFormat[data.view];
|
||||
this.showTime = data.view == 'date' ? false : true;
|
||||
// update-begin--author:liaozhiyang---date:20240430---for:【issues/6094】online 日期(年月日)控件增加年、年月,年周,年季度等格式
|
||||
let fieldExtendJson = data.fieldExtendJson;
|
||||
if (data.view == 'date' && fieldExtendJson) {
|
||||
fieldExtendJson = JSON.parse(fieldExtendJson);
|
||||
if (fieldExtendJson.picker && fieldExtendJson.picker != 'default') {
|
||||
this.picker = fieldExtendJson.picker;
|
||||
} else {
|
||||
this.picker = undefined;
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240430---for:【issues/6094】online 日期(年月日)控件增加年、年月,年周,年季度等格式
|
||||
// 只有等于和不等于才能选择预设范围(今天、昨天、本周等)
|
||||
this.allowSelectRange = ['eq', 'ne'].includes(queryItem?.rule)
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'DatePickerInFilter',
|
||||
componentProps: {
|
||||
placeholder: `请选择${this.label}`,
|
||||
showTime: this.showTime,
|
||||
valueFormat: this.format,
|
||||
allowSelectRange: this.allowSelectRange,
|
||||
// update-begin--author:liaozhiyang---date:20240430---for:【issues/6094】online 日期(年月日)控件增加年、年月,年周,年季度等格式
|
||||
picker: this.picker,
|
||||
// update-end--author:liaozhiyang---date:20240430---for:【issues/6094】online 日期(年月日)控件增加年、年月,年周,年季度等格式
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
getPopupContainer: (_node) => {
|
||||
return this.getModalAsContainer();
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
/**
|
||||
* 富文本
|
||||
*/
|
||||
export default class EditorWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JEditor',
|
||||
componentProps: {
|
||||
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1159 第一次加载时,点击第一个输入框,光标会跑到富文本输入框
|
||||
options: {
|
||||
auto_focus: false,
|
||||
},
|
||||
//update-end-author:taoyan date:2022-6-1 for: VUEN-1159 第一次加载时,点击第一个输入框,光标会跑到富文本输入框
|
||||
// fileMax:1,
|
||||
// showImageUpload:false,
|
||||
// width:"966px",
|
||||
// height:"200px"
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 文件
|
||||
*/
|
||||
export default class FileWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JUpload',
|
||||
componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let json = this.getExtendData();
|
||||
if (json && json.uploadnum) {
|
||||
return {
|
||||
maxCount: Number(json.uploadnum),
|
||||
};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { UploadTypeEnum } from '/@/components/Form/src/jeecg/components/JUpload';
|
||||
|
||||
/**
|
||||
* 图片
|
||||
*/
|
||||
export default class ImageWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JUpload',
|
||||
componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let props = {
|
||||
fileType: UploadTypeEnum.image,
|
||||
};
|
||||
let json = this.getExtendData();
|
||||
if (json && json.uploadnum) {
|
||||
props['maxCount'] = Number(json.uploadnum);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 输入框
|
||||
*/
|
||||
export default class InputWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
if (this.hidden === true) {
|
||||
item['show'] = false;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 下拉联动- 原理是:
|
||||
* 使用JDictSelectTag组件(2022-03-09测试可行版 后续如有改动请注意)
|
||||
* 监听表单的change事件,清空下级表单值,并改变props
|
||||
* 问题在于1.没有code的时候 不需要设置选项
|
||||
* 优势在于: 可以不考虑组件位置(但是需要改后台接口)
|
||||
*/
|
||||
export default class LinkDownWidget extends IFormSchema {
|
||||
/*title-value*/
|
||||
options: any[];
|
||||
next: string;
|
||||
type: string;
|
||||
table: string;
|
||||
txt: string;
|
||||
store: string;
|
||||
pidField: string;
|
||||
idField: string;
|
||||
origin: boolean;
|
||||
condition: string;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
const { dictTable, dictText, dictCode, pidField, idField, origin, condition } = data;
|
||||
this.table = dictTable;
|
||||
this.txt = dictText;
|
||||
this.store = dictCode;
|
||||
this.idField = idField;
|
||||
this.pidField = pidField;
|
||||
this.origin = origin;
|
||||
this.condition = condition;
|
||||
// 都是空数组
|
||||
this.options = [];
|
||||
this.next = data.next || '';
|
||||
this.type = data.type;
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'OnlineSelectCascade',
|
||||
componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let baseProp = {
|
||||
table: this.table,
|
||||
txt: this.txt,
|
||||
store: this.store,
|
||||
pidField: this.pidField,
|
||||
idField: this.idField,
|
||||
origin: this.origin,
|
||||
pidValue: '-1',
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
onChange: (value) => {
|
||||
console.log('级联组件-onChange', value);
|
||||
this.valueChange(value);
|
||||
},
|
||||
onNext: (pidValue) => {
|
||||
console.log('级联组件-onNext', pidValue);
|
||||
this.nextOptionsChange(pidValue);
|
||||
},
|
||||
};
|
||||
if (this._data.origin === true) {
|
||||
baseProp['condition'] = this.condition;
|
||||
}
|
||||
return baseProp;
|
||||
}
|
||||
|
||||
async nextOptionsChange(pidValue) {
|
||||
if (!this.formRef) {
|
||||
console.error('表单引用找不到');
|
||||
return;
|
||||
}
|
||||
if (!this.next) {
|
||||
return;
|
||||
}
|
||||
let ref = this.formRef.value;
|
||||
await ref.updateSchema({
|
||||
field: this.next,
|
||||
componentProps: {
|
||||
pidValue,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async valueChange(value) {
|
||||
if (!this.formRef) {
|
||||
console.error('表单引用找不到');
|
||||
return;
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240717---for:【TV360X-1856】联动组件最后一个js增强onchang方法不生效
|
||||
let ref = this.formRef.value;
|
||||
// 触发form层级的change事件
|
||||
ref.$formValueChange(this.field, value);
|
||||
if (this.next) {
|
||||
// 重置value
|
||||
await ref.setFieldsValue({ [this.next]: '' });
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240717---for:【TV360X-1856】联动组件最后一个js增强onchang方法不生效
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 他表字段
|
||||
*/
|
||||
export default class LinkTableFieldWidget extends IFormSchema {
|
||||
|
||||
dictTable: string;
|
||||
dictText: string;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.dictTable = data['dictTable'];
|
||||
this.dictText = data['dictText'];
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
componentProps: {
|
||||
readOnly: true,
|
||||
allowClear: false,
|
||||
disabled: true,
|
||||
style:{
|
||||
background: 'none',
|
||||
color:'rgba(0, 0, 0, 0.85)',
|
||||
border:'none'
|
||||
}
|
||||
}
|
||||
});
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取他表字段的关联信息
|
||||
*/
|
||||
getLinkFieldInfo(){
|
||||
let arr = [this.dictTable, `${this.field},${this.dictText}`];
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 表单设计器-关联记录查询 使用下拉搜索
|
||||
*/
|
||||
export default class LinkTableForQueryWidget extends IFormSchema {
|
||||
|
||||
code: string;
|
||||
titleField: string;
|
||||
multi: boolean;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.code = data['code'];
|
||||
this.titleField = data['titleField'];
|
||||
this.multi = data['multi']||false;
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'LinkTableForQuery',
|
||||
componentProps:{
|
||||
code: this.code,
|
||||
multi: this.multi,
|
||||
field: this.titleField,
|
||||
style: {
|
||||
width: '100%',
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 关联记录
|
||||
*/
|
||||
export default class LinkTableWidget extends IFormSchema {
|
||||
dictTable: string;
|
||||
dictText: string;
|
||||
dictCode: string;
|
||||
view: string;
|
||||
componentString: string;
|
||||
linkFields: Array<string>;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.dictTable = data.dictTable;
|
||||
this.dictText = data.dictText;
|
||||
this.dictCode = data.dictCode;
|
||||
this.view = data.view;
|
||||
this.componentString = ''
|
||||
this.linkFields = []
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
const componentProps = this.getComponentProps()
|
||||
return Object.assign({}, item, {
|
||||
component: this.componentString,
|
||||
componentProps: componentProps
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let props = {
|
||||
textField: this.dictText,
|
||||
tableName: this.dictTable,
|
||||
valueField: this.dictCode,
|
||||
};
|
||||
let extend = this.getExtendData();
|
||||
// 是否多选
|
||||
if (extend.multiSelect) {
|
||||
props['multi'] = true;
|
||||
}else{
|
||||
props['multi'] = false;
|
||||
}
|
||||
//封面图
|
||||
if (extend.imageField) {
|
||||
props['imageField'] = extend.imageField;
|
||||
}else{
|
||||
props['imageField'] = ''
|
||||
}
|
||||
//显示类型
|
||||
if (extend.showType=='select') {
|
||||
this.componentString = 'LinkTableSelect'
|
||||
let popContainer = this.getPopContainer();
|
||||
props['popContainer'] = popContainer
|
||||
}else{
|
||||
this.componentString = 'LinkTableCard'
|
||||
}
|
||||
if(this.linkFields.length>0){
|
||||
props['linkFields'] = this.linkFields;
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
// 他表字段用于翻译
|
||||
setOtherInfo(arr){
|
||||
// ["表单字段,表字典字段","表单字段,表字典字段"]
|
||||
this.linkFields = arr;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
/**
|
||||
* markdown
|
||||
*/
|
||||
export default class MarkdownWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JMarkdownEditor',
|
||||
componentProps: {
|
||||
// height: 300,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 输入框-数字
|
||||
*/
|
||||
export default class NumberWidget extends IFormSchema {
|
||||
dbPointLength: number;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.dbPointLength = data.dbPointLength;
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
const safeIntRule = {
|
||||
validator: (_rule, value) => {
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
if (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) {
|
||||
return Promise.reject(`数值超出安全范围(${Number.MIN_SAFE_INTEGER}~${Number.MAX_SAFE_INTEGER}),精度将丢失,请重新输入`);
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
};
|
||||
const existingRules = item.rules || [];
|
||||
return Object.assign({}, item, {
|
||||
component: 'InputNumber',
|
||||
componentProps,
|
||||
// update-begin--author:liaozhiyang---date:20260413---for:【QQYUN-9790】online中数字类型超出js语言数值范围加提示
|
||||
rules: [...existingRules, safeIntRule],
|
||||
// update-end--author:liaozhiyang---date:20260413---for:【QQYUN-9790】online中数字类型超出js语言数值范围加提示
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
const props = {
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
};
|
||||
if (this.dbPointLength >= 0) {
|
||||
props['precision'] = this.dbPointLength;
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 输入框- 密码
|
||||
*/
|
||||
export default class PasswordWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'InputPassword',
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
/**
|
||||
* 省市区
|
||||
*/
|
||||
export default class PcaWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JAreaSelect',
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
import {unref} from 'vue'
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* popupDict
|
||||
*/
|
||||
export default class PopupDictWidget extends IFormSchema {
|
||||
dictCode: string;
|
||||
multi: boolean;
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.dictCode = `${data['code']},${data['destFields']},${data['orgFields']}`;
|
||||
this.multi = data['popupMulti'];
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
const item = super.getItem();
|
||||
const componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JPopupDict',
|
||||
componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
const props = {
|
||||
dictCode: this.dictCode,
|
||||
multi: this.multi,
|
||||
};
|
||||
// 解决表单设计器高级查询 popup组件弹窗导致高级查询pop关闭
|
||||
if (this.inPopover) {
|
||||
props['getContainer'] = () => {
|
||||
return this.getModalAsContainer();
|
||||
};
|
||||
}
|
||||
|
||||
// 获取表单数据
|
||||
props['getFormValues'] = () => unref(this.formRef).getFieldsValue();
|
||||
|
||||
return props;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
import {unref} from 'vue'
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* popup
|
||||
*/
|
||||
export default class PopupWidget extends IFormSchema {
|
||||
code: string;
|
||||
multi: boolean;
|
||||
fieldConfig: any[];
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.code = data['code'];
|
||||
this.multi = data['popupMulti'];
|
||||
this.fieldConfig = this.getFieldConfig(data);
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JPopup',
|
||||
componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let props = {
|
||||
code: this.code,
|
||||
multi: this.multi,
|
||||
fieldConfig: this.fieldConfig,
|
||||
};
|
||||
if (this.formRef) {
|
||||
props['formElRef'] = this.formRef;
|
||||
} else {
|
||||
props['setFieldsValue'] = this.setFieldsValue;
|
||||
}
|
||||
// 解决表单设计器高级查询 popup组件弹窗导致高级查询pop关闭
|
||||
if(this.inPopover === true){
|
||||
props['getContainer'] = ()=>{
|
||||
return this.getModalAsContainer();
|
||||
}
|
||||
}
|
||||
|
||||
// 获取表单数据
|
||||
props['getFormValues'] = () => unref(this.formRef).getFieldsValue();
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
getFieldConfig(data) {
|
||||
let { destFields, orgFields, dictText } = data;
|
||||
if (!destFields || destFields.length == 0) {
|
||||
return [];
|
||||
}
|
||||
let arr1 = destFields.split(',');
|
||||
let arr2 = orgFields.split(',');
|
||||
let arr3 = dictText ? dictText.split(',') : null;
|
||||
let config: any[] = [];
|
||||
const pre = this.pre;
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
config.push({
|
||||
target: pre + arr1[i],
|
||||
source: arr2[i],
|
||||
label: arr3 ? arr3[i] : void 0,
|
||||
});
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* radio
|
||||
* 没有现成的 只能借用JDictSelectTag
|
||||
*/
|
||||
export default class RadioWidget extends IFormSchema {
|
||||
dictTable: string;
|
||||
dictText: string;
|
||||
dictCode: string;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
// 可以从这个里面取 但是换成临时加载的
|
||||
//this.options = this.getOptions(data['enum'])
|
||||
this.dictTable = data['dictTable'];
|
||||
this.dictText = data['dictText'];
|
||||
this.dictCode = data['dictCode'];
|
||||
}
|
||||
|
||||
setFormRef(ref) {
|
||||
super.setFormRef(ref);
|
||||
this.handleDictTableParams();
|
||||
}
|
||||
|
||||
updateDictTable(dictTable: string) {
|
||||
this.formRef.value.updateSchema(({
|
||||
field: this.field,
|
||||
componentProps: {
|
||||
dictCode: this.genDictTableCode(dictTable, this.dictText, this.dictCode),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JDictSelectTag',
|
||||
componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
if (!this.dictTable && !this.dictCode) {
|
||||
// 字典表 和 字典 都没填数据
|
||||
return {};
|
||||
} else {
|
||||
if (!this.dictTable) {
|
||||
return {
|
||||
// update-begin--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
useDicColor: true,
|
||||
// update-end--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
dictCode: this.dictCode,
|
||||
type: 'radio',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
dictCode: this.genDictTableCode(this.dictTable, this.dictText, this.dictCode),
|
||||
type: 'radio',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 日期、时间、数值-范围
|
||||
*/
|
||||
export default class RangeWidget extends IFormSchema {
|
||||
|
||||
componentType: string;
|
||||
datetime: boolean;
|
||||
format: string;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
let view = data.view;
|
||||
this.format = data.format;
|
||||
this.datetime = false;
|
||||
if('rangeNumber'===view){
|
||||
this.componentType = 'JRangeNumber'
|
||||
}else if('rangeTime'===view){
|
||||
this.componentType = 'RangeTime'
|
||||
}else{
|
||||
this.componentType = 'RangeDate'
|
||||
if(data.datetime===true){
|
||||
this.datetime = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: this.componentType,
|
||||
componentProps: {
|
||||
datetime: this.datetime,
|
||||
format: this.format,
|
||||
getPopupContainer: (_node) => {
|
||||
return this.getModalAsContainer();
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
/**
|
||||
* 部门选择
|
||||
*/
|
||||
export default class SelectDepartWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JSelectDept',
|
||||
componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let extend = this.getExtendData();
|
||||
let props = {
|
||||
// update-begin--author:liaozhiyang---date:20260414---for:【QQYUN-9801】修复online点击展开全部,树节点没全部展开
|
||||
sync: false,
|
||||
// update-end--author:liaozhiyang---date:20260414---for:【QQYUN-9801】修复online点击展开全部,树节点没全部展开
|
||||
checkStrictly: true,
|
||||
showButton: false,
|
||||
};
|
||||
if (extend.text) {
|
||||
props['labelKey'] = extend.text;
|
||||
}
|
||||
if (extend.store) {
|
||||
props['rowKey'] = extend.store;
|
||||
}
|
||||
if (extend.multiSelect === false) {
|
||||
props['multiple'] = false;
|
||||
}
|
||||
|
||||
if (extend.multiSelect === true) {
|
||||
props['multiple'] = true;
|
||||
}
|
||||
props['maxTagCount'] = 3;
|
||||
|
||||
// 解决表单设计器高级查询 popup组件弹窗导致高级查询pop关闭
|
||||
if(this.inPopover === true){
|
||||
props['getContainer'] = ()=>{
|
||||
return this.getModalAsContainer();
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 下拉多选框
|
||||
*/
|
||||
export default class SelectMultiWidget extends IFormSchema {
|
||||
dictTable: string;
|
||||
dictText: string;
|
||||
dictCode: string;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
// 可以从这个里面取 但是换成临时加载的
|
||||
//this.options = this.getOptions(data['enum'])
|
||||
this.dictTable = data['dictTable'];
|
||||
this.dictText = data['dictText'];
|
||||
this.dictCode = data['dictCode'];
|
||||
}
|
||||
|
||||
setFormRef(ref) {
|
||||
super.setFormRef(ref);
|
||||
this.handleDictTableParams();
|
||||
}
|
||||
|
||||
updateDictTable(dictTable: string) {
|
||||
this.formRef.value.updateSchema(({
|
||||
field: this.field,
|
||||
componentProps: {
|
||||
dictCode: this.genDictTableCode(dictTable, this.dictText, this.dictCode)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JSelectMultiple',
|
||||
componentProps: componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
if (!this.dictTable && !this.dictCode) {
|
||||
// 字典表 和 字典 都没填数据
|
||||
return {};
|
||||
} else {
|
||||
let props = {};
|
||||
if (!this.dictTable) {
|
||||
props['dictCode'] = this.dictCode;
|
||||
// update-begin--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
props['useDicColor'] = true;
|
||||
// update-end--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
} else {
|
||||
props['dictCode'] = this.genDictTableCode(this.dictTable, this.dictText, this.dictCode);
|
||||
// update-begin--author:liaozhiyang---date:20260204---for:【issues/9307】online下拉加载表字典需滚动加载
|
||||
// 默认滚动加载字典表数据
|
||||
props['scrollLoad'] = true;
|
||||
// update-end--author:liaozhiyang---date:20260204---for:【issues/9307】online下拉加载表字典需滚动加载
|
||||
}
|
||||
props['triggerChange'] = true;
|
||||
props['popContainer'] = this.getPopContainer();
|
||||
return props;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 下拉搜索
|
||||
*/
|
||||
export default class SelectSearchWidget extends IFormSchema {
|
||||
dict: string;
|
||||
type: number;
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
if (data.dictTable && data.dictText && data.dictCode) {
|
||||
// 字典表
|
||||
this.dict = this.genDictTableCode(data.dictTable, data.dictText, data.dictCode);
|
||||
this.type = 1;
|
||||
} else {
|
||||
// 数据字典
|
||||
this.dict = encodeURI(`${data.dictCode}`);
|
||||
this.type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
setFormRef(ref) {
|
||||
super.setFormRef(ref);
|
||||
this.handleDictTableParams();
|
||||
}
|
||||
|
||||
updateDictTable(dictTable: string) {
|
||||
this.formRef.value.updateSchema(({
|
||||
field: this.field,
|
||||
componentProps: {
|
||||
dict: this.genDictTableCode(dictTable, this._data.dictText, this._data.dictCode),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let popContainer = this.getPopContainer();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JSearchSelect',
|
||||
componentProps: {
|
||||
dict: this.dict,
|
||||
pageSize: 10,
|
||||
// update-begin--author:liaozhiyang---date:20240628---for:【issues/6336】online下拉搜索框设置数据字典编辑弹窗报错
|
||||
async: this.type ? true : false,
|
||||
// update-end--author:liaozhiyang---date:20240628---for:【issues/6336】online下拉搜索框设置数据字典编辑弹窗报错
|
||||
useDicColor: true,
|
||||
popContainer: popContainer,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
/**
|
||||
* 用户选择
|
||||
*/
|
||||
export default class SelectUser2Widget extends IFormSchema {
|
||||
|
||||
multi: boolean;
|
||||
store: string;
|
||||
query: boolean;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.multi = data.multi === true ? true : false;
|
||||
this.store = data.store||'';
|
||||
// 是否是查询条件,查询条件显示为输入框样式
|
||||
this.query = data.query||false;
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'UserSelect',
|
||||
componentProps
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let props = {
|
||||
multi: this.multi,
|
||||
store: this.store,
|
||||
query: this.query,
|
||||
}
|
||||
// 解决表单设计器高级查询 popup组件弹窗导致高级查询pop关闭
|
||||
if(this.inPopover === true){
|
||||
props['getContainer'] = ()=>{
|
||||
return this.getModalAsContainer();
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
/**
|
||||
* 用户选择
|
||||
*/
|
||||
export default class SelectUserWidget extends IFormSchema {
|
||||
|
||||
showButton: boolean;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.showButton = data.showButton === false ? false : true;
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JSelectUser',
|
||||
componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let extend = this.getExtendData();
|
||||
let props = {
|
||||
showSelected: false,
|
||||
allowClear: true,
|
||||
isRadioSelection: false,
|
||||
showButton: this.showButton
|
||||
};
|
||||
if (extend.text) {
|
||||
props['labelKey'] = extend.text;
|
||||
}
|
||||
if (extend.store) {
|
||||
props['rowKey'] = extend.store;
|
||||
}
|
||||
if (extend.multiSelect === false) {
|
||||
//props['multiple'] = false
|
||||
props['isRadioSelection'] = true;
|
||||
}
|
||||
props['maxTagCount'] = 3;
|
||||
|
||||
// 解决表单设计器高级查询 popup组件弹窗导致高级查询pop关闭
|
||||
if(this.inPopover === true){
|
||||
props['getContainer'] = ()=>{
|
||||
return this.getModalAsContainer();
|
||||
}
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,150 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import { h } from 'vue';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 下拉框
|
||||
* //待处理: 表字典取数据可以考虑传参前端再请求
|
||||
*/
|
||||
export default class SelectWidget extends IFormSchema {
|
||||
schema: Recordable;
|
||||
/*title-value*/
|
||||
options: any[];
|
||||
dictTable: string;
|
||||
dictText: string;
|
||||
dictCode: string;
|
||||
multi: boolean;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.schema = data;
|
||||
// 静态数据选项(enum)转换为 JSelectSingle 所需格式
|
||||
this.options = data['enum'] ? this.getOptions(data['enum'], '') : [];
|
||||
this.dictTable = data['dictTable'];
|
||||
this.dictText = data['dictText'];
|
||||
this.dictCode = data['dictCode'];
|
||||
this.multi = data['multi'] || false;
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let component = this.getFormComponent()
|
||||
let componentProps = this.getComponentProps()
|
||||
return Object.assign({}, item, {
|
||||
component,
|
||||
componentProps,
|
||||
renderComponentContent: this.getSlots(componentProps),
|
||||
});
|
||||
}
|
||||
|
||||
getFormComponent(){
|
||||
// update-begin--author:liaozhiyang---date:20260204---for:【issues/9307】online下拉加载表字典需滚动加载
|
||||
// if(this.options.length>0){
|
||||
// return 'Select'
|
||||
// }else{
|
||||
// return 'JDictSelectTag'
|
||||
// }
|
||||
return 'JSelectSingle'
|
||||
// update-end--author:liaozhiyang---date:20260204---for:【issues/9307】online下拉加载表字典需滚动加载
|
||||
}
|
||||
|
||||
setFormRef(ref) {
|
||||
super.setFormRef(ref);
|
||||
this.handleDictTableParams();
|
||||
}
|
||||
|
||||
updateDictTable(dictTable: string) {
|
||||
this.formRef.value.updateSchema(({
|
||||
field: this.field,
|
||||
componentProps: {
|
||||
dictCode: this.genDictTableCode(dictTable, this.dictText, this.dictCode),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let mode = this.multi===true?'multiple':'combobox'
|
||||
let props: any = {
|
||||
allowClear: true,
|
||||
mode,
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
getPopupContainer: (_node) => {
|
||||
return this.getModalAsContainer();
|
||||
},
|
||||
// update-begin--author:liaozhiyang---date:20260203---for:【issues/9307】online下拉加载表字典需滚动加载
|
||||
// 下拉框展开/关闭的回调
|
||||
// onDropdownVisibleChange: (visible: boolean)=> {
|
||||
// if (visible && typeof this.schema.updateOptions === 'function') {
|
||||
// this.schema.updateOptions()
|
||||
// }
|
||||
// },
|
||||
// update-end--author:liaozhiyang---date:20260203---for:【issues/9307】online下拉加载表字典需滚动加载
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20260203---for:【issues/9307】online下拉加载表字典需滚动加载
|
||||
if (!this.dictTable) {
|
||||
props['dictCode'] = this.dictCode;
|
||||
// update-begin--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
props['useDicColor'] = true;
|
||||
// update-end--author:liaozhiyang---date:20230110---for:【QQYUN-7799】字典组件(原生组件除外)加上颜色配置
|
||||
// 静态数据(无 dictCode)时,将 enum 选项直接传给组件
|
||||
if (!this.dictCode && this.options.length > 0) {
|
||||
props['options'] = this.options;
|
||||
}
|
||||
} else {
|
||||
props['dictCode'] = this.genDictTableCode(this.dictTable, this.dictText, this.dictCode);
|
||||
props['scrollLoad'] = true;
|
||||
delete props.onDropdownVisibleChange;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260203---for:【issues/9307】online下拉加载表字典需滚动加载
|
||||
return props
|
||||
}
|
||||
|
||||
getSlots(componentProps: Recordable) {
|
||||
const {useDicColor} = componentProps;
|
||||
return function () {
|
||||
return {
|
||||
option(option: Recordable) {
|
||||
const style: Recordable = {};
|
||||
if (useDicColor && option.color) {
|
||||
style.color = '#fff';
|
||||
style.height = '20px';
|
||||
style.lineHeight = '20px';
|
||||
style.padding = '0 6px';
|
||||
style.fontSize = '12px';
|
||||
style.borderRadius = '8px';
|
||||
style.backgroundColor = option.color;
|
||||
style.display = 'inline-block';
|
||||
}
|
||||
return h('span', {
|
||||
style,
|
||||
}, option.text || option.label);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getOptions(array, type) {
|
||||
if (!array || array.length == 0) {
|
||||
return [];
|
||||
}
|
||||
let isNum = 'number' == type;
|
||||
let arr: any[] = [];
|
||||
for (let item of array) {
|
||||
// update-begin--author:liaozhiyang---date:20240517---for:【QQYUN-9359】加强判断,防止数据有null报错
|
||||
if (item == null) break;
|
||||
// update-end--author:liaozhiyang---date:20240517---for:【QQYUN-9359】加强判断,防止数据有null报错
|
||||
let value = item.value;
|
||||
if(isNum){
|
||||
value = parseInt(value)
|
||||
}
|
||||
arr.push({
|
||||
...item,
|
||||
value,
|
||||
label: item.title,
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* slot
|
||||
*/
|
||||
export default class SlotWidget extends IFormSchema {
|
||||
slot: string;
|
||||
picker: string | undefined;
|
||||
precision: number | undefined;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.slot = '';
|
||||
// update-begin--author:liaozhiyang---date:20240520---for:【TV360X-180】范围查询年,年月,周,季度
|
||||
let fieldExtendJson = data.fieldExtendJson;
|
||||
if (data.view == 'date' && fieldExtendJson) {
|
||||
fieldExtendJson = JSON.parse(fieldExtendJson);
|
||||
if (fieldExtendJson.picker && fieldExtendJson.picker != 'default') {
|
||||
this.picker = fieldExtendJson.picker;
|
||||
} else {
|
||||
this.picker = undefined;
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240520---for:【TV360X-180】范围查询年,年月,周,季度
|
||||
// update-begin--author:liaozhiyang---date:20240606---for:【TV360X-214】范围查询控件没有根据配置格式化
|
||||
this.precision = data.dbPointLength;
|
||||
// update-end--author:liaozhiyang---date:20240606---for:【TV360X-214】范围查询控件没有根据配置格式化
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let slot = this.slot;
|
||||
const componentProps: any = {};
|
||||
this.picker && (componentProps.picker = this.picker);
|
||||
// update-begin--author:liaozhiyang---date:20240606---for:【TV360X-214】范围查询控件没有根据配置格式化
|
||||
this.precision && (componentProps.precision = this.precision);
|
||||
// update-end--author:liaozhiyang---date:20240606---for:【TV360X-214】范围查询控件没有根据配置格式化
|
||||
// update-begin--author:liaozhiyang---date:20240520---for:【TV360X-180】范围查询年,年月,周,季度
|
||||
return Object.assign({}, item, {
|
||||
slot,
|
||||
componentProps,
|
||||
});
|
||||
// update-end--author:liaozhiyang---date:20240520---for:【TV360X-180】范围查询年,年月,周,季度
|
||||
}
|
||||
|
||||
groupDate() {
|
||||
this.slot = 'groupDate';
|
||||
return this;
|
||||
}
|
||||
|
||||
groupDatetime() {
|
||||
this.slot = 'groupDatetime';
|
||||
return this;
|
||||
}
|
||||
|
||||
groupTime() {
|
||||
// update-begin--author:liaozhiyang---date:20240517---for:【QQYUN-9348】增加online查询区域时间范围查询功能
|
||||
this.slot = 'groupTime';
|
||||
return this;
|
||||
// update-end--author:liaozhiyang---date:20240517---for:【QQYUN-9348】增加online查询区域时间范围查询功能
|
||||
}
|
||||
|
||||
groupNumber() {
|
||||
this.slot = 'groupNumber';
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { isArray, isObject } from '/@/utils/is';
|
||||
|
||||
/**
|
||||
* 开关
|
||||
*/
|
||||
export default class SwitchWidget extends IFormSchema {
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
// update-begin--author:liaozhiyang---date:20240517---for:【TV360X-54】开关只读未生效
|
||||
// this.hasChange = false;
|
||||
// update-end--author:liaozhiyang---date:20240517---for:【TV360X-54】开关只读未生效
|
||||
}
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JSwitch',
|
||||
componentProps,
|
||||
});
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
let { fieldExtendJson } = this._data;
|
||||
let options = ['Y', 'N'];
|
||||
if (fieldExtendJson) {
|
||||
if (typeof fieldExtendJson == 'string') {
|
||||
// update-begin--author:liaozhiyang---date:20240522---for:【TV360X-25】扩展参数配置中增加开关是否选项配置
|
||||
const json = JSON.parse(fieldExtendJson);
|
||||
if (isArray(json) && json.length == 2) {
|
||||
options = json;
|
||||
} else if (isObject(json) && isArray(json.switchOptions)) {
|
||||
options = json.switchOptions;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240522---for:【TV360X-25】扩展参数配置中增加开关是否选项配置
|
||||
}
|
||||
}
|
||||
return {
|
||||
options,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 输入框-textarea
|
||||
*/
|
||||
export default class TextAreaWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'InputTextArea',
|
||||
componentProps:{
|
||||
autoSize : {
|
||||
minRows: 4, maxRows: 10
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 日期、时间
|
||||
*/
|
||||
export default class TimeWidget extends IFormSchema {
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'TimePicker',
|
||||
componentProps: {
|
||||
placeholder: `请选择${this.label}`,
|
||||
valueFormat: 'HH:mm:ss',
|
||||
getPopupContainer: (_node) => {
|
||||
return this.getModalAsContainer();
|
||||
},
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import IFormSchema from '../IFormSchema';
|
||||
|
||||
/**
|
||||
* 分类字典
|
||||
*/
|
||||
export default class TreeCategoryWidget extends IFormSchema {
|
||||
pid: string;
|
||||
multi: boolean;
|
||||
textField: string;
|
||||
pcode: string;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.multi = false;
|
||||
this.pid = data['pidValue'];
|
||||
this.pcode = data['pcode'];
|
||||
this.textField = data['textField'];
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
let componentProps = this.getComponentProps();
|
||||
return Object.assign({}, item, {
|
||||
componentProps,
|
||||
component: 'JCategorySelect',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. 不带返回值的
|
||||
* 2. 带文本返回的
|
||||
*/
|
||||
getComponentProps() {
|
||||
// VUEN-1049 分类字典保存后,列表不展示 单表 树表 --> 配错编码后,表单界面还显示分类字典选项,可直接不显示字典选项
|
||||
let param = {
|
||||
placeholder: '请选择' + this.label
|
||||
}
|
||||
if(this.pcode){
|
||||
param['pcode'] = this.pcode;
|
||||
}else{
|
||||
let pidValue = this.pid || 'EMPTY_PID';
|
||||
param['pid'] = pidValue;
|
||||
}
|
||||
if (!this.textField) {
|
||||
return {
|
||||
multiple: this.multi,
|
||||
...param
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
loadTriggleChange: true,
|
||||
multiple: this.multi,
|
||||
...param,
|
||||
back: this.textField,
|
||||
onChange: (val, backVal) => {
|
||||
if (this.formRef) {
|
||||
this.formRef.value.setFieldsValue(backVal);
|
||||
this.formRef.value.$formValueChange(this.field, val)
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getRelatedHideFields(): string[] {
|
||||
let arr: string[] = [];
|
||||
if (this.textField) {
|
||||
arr.push(this.textField);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
import IFormSchema from '../IFormSchema';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
/**
|
||||
* 自定义树
|
||||
*/
|
||||
export default class TreeSelectWidget extends IFormSchema {
|
||||
/*表名、显示字段、存储字段*/
|
||||
dict: string;
|
||||
/*父级ID的字段名*/
|
||||
pidField: string;
|
||||
/*父级ID的字段值*/
|
||||
pidValue: string;
|
||||
/*是否有子节点*/
|
||||
hasChildField: string;
|
||||
|
||||
constructor(key, data) {
|
||||
super(key, data);
|
||||
this.dict = data['dict'];
|
||||
this.pidField = data['pidField'];
|
||||
this.pidValue = data['pidValue'];
|
||||
// update-begin--author:liaozhiyang---date:20240509---for:【issues/6197】解决自定义树组件是否含有子节点功能不生效
|
||||
this.hasChildField = data['hasChildField'];
|
||||
// update-end--author:liaozhiyang---date:20240509---for:【issues/6197】解决自定义树组件是否含有子节点功能不生效
|
||||
}
|
||||
|
||||
getItem(): FormSchema {
|
||||
let item = super.getItem();
|
||||
return Object.assign({}, item, {
|
||||
component: 'JTreeSelect',
|
||||
componentProps: {
|
||||
dict: this.dict,
|
||||
pidField: this.pidField,
|
||||
pidValue: this.pidValue,
|
||||
// update-begin--author:liaozhiyang---date:20240509---for:【issues/6197】解决自定义树组件是否含有子节点功能不生效
|
||||
hasChildField: this.hasChildField,
|
||||
// update-end--author:liaozhiyang---date:20240509---for:【issues/6197】解决自定义树组件是否含有子节点功能不生效
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import type { App } from 'vue';
|
||||
import {defineAsyncComponent} from 'vue'
|
||||
const SuperQuery = defineAsyncComponent(() => import('./superquery/SuperQuery.vue'))
|
||||
const JOnlineSearchSelect = defineAsyncComponent(() => import('./JOnlineSearchSelect.vue'))
|
||||
|
||||
export const registerOnlineComp = {
|
||||
install(app: App) {
|
||||
app.component('JOnlineSearchSelect', JOnlineSearchSelect);
|
||||
app.component('SuperQuery', SuperQuery);
|
||||
|
||||
console.log("---初始化---, 全局注册Online部分组件--------------")
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,716 @@
|
||||
<template>
|
||||
<!-- 按钮区域 -->
|
||||
<div class="j-super-query-button">
|
||||
<a-tooltip v-if="superQueryFlag" :mouseLeaveDelay="0.2">
|
||||
<template #title>
|
||||
<span>执行查询中...</span>
|
||||
<divider type="vertical" style="background-color: #fff"/>
|
||||
<a @click="handleStop">取消查询</a>
|
||||
</template>
|
||||
<a-button-group>
|
||||
<a-button type="primary" @click="handleOpen">
|
||||
<AppstoreTwoTone :spin="true"/>
|
||||
<span>{{queryBtnCfg.buttonName}}</span>
|
||||
</a-button>
|
||||
</a-button-group>
|
||||
</a-tooltip>
|
||||
<a-button v-else type="primary" :preIcon="queryBtnCfg.buttonIcon" @click="handleOpen">
|
||||
<span>{{queryBtnCfg.buttonName}}</span>
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<!-- 高级查询弹框 -->
|
||||
<teleport to="body">
|
||||
<BasicModal :title="queryBtnCfg.buttonName + '构造器'" wrap-class-name="j-super-query-modal" :canFullscreen="true" :width="850" @register="registerFormModal" @ok="handleSubmit" @fullScreen="handleFullScreen">
|
||||
<template #footer>
|
||||
<div style="float: left">
|
||||
<a-button :loading="loading" @click="handleReset">清空</a-button>
|
||||
<a-button :loading="loading" @click="handleSave">保存查询</a-button>
|
||||
</div>
|
||||
|
||||
<a-button key="submit" type="primary" @click="handleSubmit">执行查询</a-button>
|
||||
<a-button key="back" @click="handleCancel">关闭</a-button>
|
||||
</template>
|
||||
|
||||
<a-empty v-if="dynamicRowValues.values.length == 0">
|
||||
<div slot="description">
|
||||
<span>没有任何查询条件</span>
|
||||
<a-divider type="vertical"/>
|
||||
<a @click="addOne(-1)">点击新增</a>
|
||||
</div>
|
||||
</a-empty>
|
||||
|
||||
<a-row :class="'j-super-query-modal-content'">
|
||||
<a-col :sm="24" :md="24">
|
||||
<a-row v-show="dynamicRowValues.values.length > 0">
|
||||
<a-col :md="12" :xs="24">
|
||||
<a-form-item label="匹配模式" :labelCol="{md: 6,xs:24}" :wrapperCol="{md: 18,xs:24}" style="width: 100%;">
|
||||
<a-select v-model:value="matchType" :getPopupContainer="node=>node?.parentNode" style="width: 100%;">
|
||||
<a-select-option value="and">AND(所有条件匹配)</a-select-option>
|
||||
<a-select-option value="or">OR(任意一个匹配)</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form v-show="dynamicRowValues.values.length > 0" ref="formRef" :class="'jee-super-query-form'" :model="dynamicRowValues" @finish="onFinish" :style="queryFormStyle">
|
||||
<a-space v-for="(item, index) in dynamicRowValues.values" :key="item.key" style="display: flex; margin-bottom: 8px" :align="item.curLineAlign ? item.align : 'baseline'">
|
||||
|
||||
<a-form-item class="field-clos" :name="['values', index, 'field']" >
|
||||
<a-tree-select
|
||||
:popupClassName="getTreePopupClass"
|
||||
style="width:100%"
|
||||
placeholder="请选择字段"
|
||||
v-model:value="item.field"
|
||||
show-search
|
||||
tree-node-filter-prop="title"
|
||||
allow-clear
|
||||
tree-default-expand-all
|
||||
:dropdown-style="{ maxHeight: `${fieldTreeSelectHeight}px`, overflow: 'auto' }"
|
||||
:listHeight="fieldTreeSelectHeight - 20"
|
||||
@change="handleChangeField(item)"
|
||||
:tree-data="fieldTreeData">
|
||||
</a-tree-select>
|
||||
</a-form-item>
|
||||
<a-form-item class="rule-clos" :name="['values', index, 'rule']">
|
||||
<a-select style="width:100%" placeholder="请选择匹配规则" v-model:value="item.rule">
|
||||
<a-select-option :value="qItem.value" v-for="qItem in getQueryCondition(item)" :key="qItem.value">{{qItem.label}}</a-select-option>
|
||||
<!-- 当前表单设计器内专用 -->
|
||||
<!-- <a-select-option value="empty">为空</a-select-option>
|
||||
<a-select-option value="not_empty">不为空</a-select-option>-->
|
||||
<!-- 当前表单设计器内专用 -->
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item class="component-clos" :name="['values', index, 'val']">
|
||||
<online-super-query-val-component style="width:100%" :schema="getSchema(item, index)" :formModel="item" :setFormModel="(key, value)=>{setFormModel(key, value, item)}" @submit="handleSubmit" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button @click="addOne(index)" style="margin-right: 6px">
|
||||
<PlusOutlined #icon/>
|
||||
</a-button>
|
||||
<a-button @click="removeOne(item)">
|
||||
<MinusCircleOutlined #icon/>
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-space>
|
||||
</a-form>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- 查询记录 -->
|
||||
<a-card :class="['j-super-query-history-card', {'collapsed': historyCollapsed}]" :bordered="false">
|
||||
<template #title><div>保存的查询</div></template>
|
||||
<a-empty
|
||||
v-if="saveTreeData.length === 0"
|
||||
class="j-super-query-history-empty"
|
||||
:image="simpleImage"
|
||||
description="没有保存的查询"
|
||||
/>
|
||||
<a-tree
|
||||
v-else
|
||||
class="j-super-query-history-tree"
|
||||
:treeData="saveTreeData"
|
||||
:selectedKeys="[]"
|
||||
:show-icon="true"
|
||||
@select="handleTreeSelect">
|
||||
<template #title="{title}">
|
||||
<div>
|
||||
<span :title="title">{{(title.length>10)?(title.substring(0, 10)+'...'):title }}</span>
|
||||
<a-popconfirm title="确定删除吗?" @confirm="handleRemoveSaveInfo(title)">
|
||||
<span class="icon-cancle" @click="(e)=>e.stopPropagation()"><close-circle-outlined /></span>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
<!-- antd-2是这么写的 升级到3会也许会改变写法 -->
|
||||
<template #custom>
|
||||
<file-text-outlined/>
|
||||
</template>
|
||||
</a-tree>
|
||||
<div class="collapse-box" @click="historyCollapsed=!historyCollapsed">
|
||||
<Icon v-if="historyCollapsed" icon="ant-design:caret-left"/>
|
||||
<Icon v-else icon="ant-design:caret-right"/>
|
||||
</div>
|
||||
</a-card>
|
||||
</BasicModal>
|
||||
</teleport>
|
||||
|
||||
<!-- 保存信息弹框 -->
|
||||
<a-modal title="请输入保存的名称" :open="saveInfo.visible" @cancel="saveInfo.visible=false" @ok="doSaveQueryInfo" :confirmLoading="saveModalLoading">
|
||||
<div style="height:80px;line-height:75px;width:100%;text-align: center">
|
||||
<a-input v-model:value="saveInfo.title" style="width:90%" placeholder="请输入保存的名称"></a-input>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
import {ref, watch, computed} from 'vue'
|
||||
import { BasicModal, useModal } from '/@/components/Modal';
|
||||
import { useSuperQuery } from './useSuperQuery';
|
||||
import OnlineSuperQueryValComponent from './SuperQueryValComponent.vue';
|
||||
import { MinusCircleOutlined, PlusOutlined, FileTextOutlined, CloseCircleOutlined, AppstoreTwoTone } from '@ant-design/icons-vue';
|
||||
import { Divider, Empty, Popconfirm } from 'ant-design-vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import dayjs from 'dayjs';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useConditionFilter } from '/@/utils/index';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
//const { BasicModal, useModal } = defineAsyncComponent(()=>import('/@/components/Modal'));
|
||||
export default{
|
||||
name: "OnlineSuperQuery",
|
||||
props: {
|
||||
config:{
|
||||
type: Object,
|
||||
default: []
|
||||
},
|
||||
status:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
online: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// update-begin--author:liaozhiyang---date:20240514---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
isCustomSave: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
saveSearchData: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
save: {
|
||||
type: Function,
|
||||
},
|
||||
// update-end--author:liaozhiyang---date:20240514---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
queryBtnCfg: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
buttonName: '高级查询',
|
||||
buttonIcon: 'ant-design:filter-outlined',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
BasicModal,
|
||||
MinusCircleOutlined,
|
||||
PlusOutlined,
|
||||
OnlineSuperQueryValComponent,
|
||||
FileTextOutlined,
|
||||
CloseCircleOutlined,
|
||||
AppstoreTwoTone,
|
||||
Divider,
|
||||
Popconfirm,
|
||||
},
|
||||
emits:['search'],
|
||||
setup(props, {emit}) {
|
||||
|
||||
console.log("-------初始化 OnlineSuperQuery注册--------------")
|
||||
|
||||
const [registerFormModal, formModal] = useModal()
|
||||
const { createMessage: $message } = useMessage();
|
||||
const queryFormStyle = ref({});
|
||||
const fieldTreeSelectHeight = ref(180);
|
||||
const { prefixCls } = useDesign('super-query');
|
||||
const treePopupClass = `${prefixCls}-tree-popup`;
|
||||
// 保存的查询是否折叠
|
||||
const historyCollapsed = ref(true)
|
||||
let currentQueryInfo = null;
|
||||
// -update-begin--author:liaozhiyang---date:20240614---for:【TV360X-76】高级查询条件根据控件类型区分
|
||||
const { filterCondition } = useConditionFilter();
|
||||
const getQueryCondition = (data) => {
|
||||
const auto = (arr, field) => {
|
||||
const findItem = arr.find((item) => item.value === field) ?? {};
|
||||
return filterCondition({ view: findItem.originView || findItem.view, fieldType: findItem.fieldType });
|
||||
};
|
||||
if (data.field?.indexOf('@') == -1 || !data.field) {
|
||||
return auto(fieldTreeData.value, data.field);
|
||||
} else {
|
||||
const tableName = data.field.split('@')[0];
|
||||
const findTableItem = fieldTreeData.value.find((item) => item.value === tableName);
|
||||
if (findTableItem?.children?.length) {
|
||||
return auto(findTableItem.children, data.field);
|
||||
}
|
||||
}
|
||||
};
|
||||
// -update-end--author:liaozhiyang---date:20240614---for:【TV360X-76】高级查询条件根据控件类型区分
|
||||
/**
|
||||
* 关闭按钮事件
|
||||
*/
|
||||
function handleCancel() {
|
||||
formModal.closeModal();
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认按钮事件
|
||||
*/
|
||||
function handleSubmit() {
|
||||
if(props.online === true){
|
||||
let dataArray = getQueryInfo(true)
|
||||
if(dataArray && dataArray.length>0){
|
||||
// update-begin--author:liaozhiyang---date:20240517---for:【TV360X-86】年,年月,周查询出结果不准
|
||||
transformDateValus(dataArray);
|
||||
// update-end--author:liaozhiyang---date:20240517---for:【TV360X-86】年,年月,周查询出结果不准
|
||||
emit('search', dataArray, matchType.value)
|
||||
// update-begin--author:liaozhiyang---date:220230802---for:【QQYUN-5995】高级查询效果后关闭弹窗
|
||||
handleCancel()
|
||||
// update-end--author:liaozhiyang---date:220230802---for:【QQYUN-5995】高级查询效果后关闭弹窗
|
||||
currentQueryInfo = dataArray;
|
||||
}else{
|
||||
$message.warning('空条件无法查询!')
|
||||
}
|
||||
}else{
|
||||
//console.log('handleSubmit', dynamicRowValues.values)
|
||||
let dataArray = getQueryInfo(true)
|
||||
if(dataArray && dataArray.length>0){
|
||||
//update-begin---author:wangshuai---date:2025-07-18---for:【issues/8548】代码生成的高级查询里日期-月控件不能正常展示---
|
||||
transformDateValus(dataArray);
|
||||
//update-end---author:wangshuai---date:2025-07-18---for:【issues/8548】代码生成的高级查询里日期-月控件不能正常展示---
|
||||
let result = getSuperQueryParams(dataArray);
|
||||
//console.log('查询数据1', dataArray)
|
||||
//console.log('查询数据2', result)
|
||||
emit('search', result)
|
||||
// update-begin--author:wangshuai---date:20251112---for:【issues/9060】superQuery高级组件,点击"查询后"不能自动关闭弹窗
|
||||
handleCancel()
|
||||
// update-end--author:wangshuai---date:20251112---for:【issues/9060】superQuery高级组件,点击"查询后"不能自动关闭弹窗
|
||||
}else{
|
||||
$message.warning('空条件无法查询!')
|
||||
}
|
||||
}
|
||||
historyCollapsed.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2024-05-17
|
||||
* liaozhiyang
|
||||
* 把年,年月,周等时间重置到当前格式的第一天,因为存的时候也是第一天
|
||||
*/
|
||||
const transformDateValus = (date) => {
|
||||
date.forEach((item) => {
|
||||
const value = item['val'];
|
||||
if (item.type === 'date' && typeof value === 'string' && value != '') {
|
||||
const obj = fieldProperties.value[item.field];
|
||||
if (obj) {
|
||||
let fieldExtendJson = obj.fieldExtendJson;
|
||||
if (fieldExtendJson) {
|
||||
fieldExtendJson = JSON.parse(fieldExtendJson);
|
||||
if (fieldExtendJson.picker && fieldExtendJson.picker !== 'default') {
|
||||
const picker = fieldExtendJson.picker;
|
||||
if (picker === 'year') {
|
||||
item['val'] = dayjs(value).set('month', 0).set('date', 1).format('YYYY-MM-DD');
|
||||
} else if (picker === 'month') {
|
||||
item['val'] = dayjs(value).set('date', 1).format('YYYY-MM-DD');
|
||||
} else if (picker === 'week') {
|
||||
item['val'] = dayjs(value).startOf('week').format('YYYY-MM-DD');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function getSuperQueryParams(dataArray){
|
||||
let arr:any = []
|
||||
for(let item of dataArray){
|
||||
let field = item.field;
|
||||
let val = item.val
|
||||
if(val instanceof Array){
|
||||
val = val.join(",")
|
||||
}
|
||||
arr.push({
|
||||
...item,
|
||||
field,
|
||||
val
|
||||
})
|
||||
}
|
||||
if(arr.length>0){
|
||||
superQueryFlag.value = true;
|
||||
}else{
|
||||
superQueryFlag.value = false;
|
||||
}
|
||||
let result = {
|
||||
superQueryMatchType: matchType.value,
|
||||
superQueryParams: encodeURI(JSON.stringify(arr))
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function handleStop(){
|
||||
let result = getSuperQueryParams([]);
|
||||
emit('search', result)
|
||||
}
|
||||
/**
|
||||
* 重置按钮事件
|
||||
*/
|
||||
function handleReset(){
|
||||
dynamicRowValues.values = []
|
||||
addOne(false);
|
||||
let result = getSuperQueryParams([])
|
||||
currentQueryInfo = null;
|
||||
historyCollapsed.value = true;
|
||||
emit('search', result)
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240524---for:【TV360X-524】高级查询增加放大功能
|
||||
const handleFullScreen = (val) => {
|
||||
// update-begin--author:liaozhiyang---date:20240603---for:【TV360X-810】高级查询放大之后树下拉框高一些多展示些字段
|
||||
if (val) {
|
||||
const contentHeight = document.documentElement.clientHeight - 165;
|
||||
queryFormStyle.value = { maxHeight: `${contentHeight}px` };
|
||||
fieldTreeSelectHeight.value = contentHeight * 0.62;
|
||||
} else {
|
||||
queryFormStyle.value = {};
|
||||
fieldTreeSelectHeight.value = 180;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240603---for:【TV360X-810】高级查询放大之后树下拉框高一些多展示些字段
|
||||
};
|
||||
// update-end--author:liaozhiyang---date:20240524---for:【TV360X-524】高级查询增加放大功能
|
||||
const {
|
||||
formRef,
|
||||
init,
|
||||
dynamicRowValues,
|
||||
matchType,
|
||||
registerModal,
|
||||
|
||||
handleSave,
|
||||
doSaveQueryInfo,
|
||||
saveInfo,
|
||||
saveTreeData,
|
||||
handleTreeSelect,
|
||||
handleRemoveSaveInfo,
|
||||
fieldTreeData,
|
||||
addOne,
|
||||
removeOne,
|
||||
setFormModel,
|
||||
getSchema,
|
||||
loading,
|
||||
getQueryInfo,
|
||||
initDefaultValues,
|
||||
saveModalLoading,
|
||||
fieldProperties,
|
||||
} = useSuperQuery(props)
|
||||
|
||||
/*--------------------按钮区域-beign------------------*/
|
||||
const superQueryFlag = ref(false)
|
||||
watch(()=>props.status, (val)=>{
|
||||
superQueryFlag.value = val;
|
||||
}, {immediate: true});
|
||||
|
||||
function handleOpen(){
|
||||
// update-begin--author:liaozhiyang---date:20240604---for:【TV360X-204】修改内容没点确定,再次打开应该是恢复之前的内容
|
||||
if (superQueryFlag.value && currentQueryInfo) {
|
||||
dynamicRowValues.values = cloneDeep(currentQueryInfo);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240604---for:【TV360X-204】修改内容没点确定,再次打开应该是恢复之前的内容
|
||||
formModal.openModal();
|
||||
historyCollapsed.value = true;
|
||||
addOne(true)
|
||||
}
|
||||
/*--------------------按钮区域-end------------------*/
|
||||
|
||||
|
||||
function getPopupContainer(){
|
||||
return document.getElementsByClassName('jee-super-query-form')[0]
|
||||
}
|
||||
function onFinish(a){
|
||||
console.log('onfinish', a)
|
||||
}
|
||||
/**
|
||||
* 2024-06-04
|
||||
* liaozhiyang
|
||||
* 【TV360X-461】字段类型是string,则默认模糊查询
|
||||
* */
|
||||
function handleChangeField(data) {
|
||||
data['val'] = '';
|
||||
const auto = (arr, field) => {
|
||||
const findItem = arr.find((item) => item.value === field);
|
||||
if (findItem?.fieldType === 'string' && ['text'].includes(findItem?.originView || findItem?.view)) {
|
||||
data['rule'] = 'like';
|
||||
} else if (['file', 'image', 'password'].includes(findItem?.originView || findItem?.view)) {
|
||||
data['rule'] = 'empty';
|
||||
} else {
|
||||
data['rule'] = 'eq';
|
||||
}
|
||||
};
|
||||
if (data.field?.indexOf('@') == -1) {
|
||||
auto(fieldTreeData.value, data.field);
|
||||
} else {
|
||||
const tableName = data.field.split('@')[0];
|
||||
const findTableItem = fieldTreeData.value.find((item) => item.value === tableName);
|
||||
if (findTableItem?.children?.length) {
|
||||
auto(findTableItem.children, data.field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
watch(()=>props.config, (val)=>{
|
||||
if(val){
|
||||
// console.log('123', val)
|
||||
// console.log('123', val)
|
||||
// console.log('123', val)
|
||||
// Object.keys(val).map(k=>{
|
||||
// console.log(k, val[k])
|
||||
// })
|
||||
// console.log('123', val)
|
||||
// console.log('123', val)
|
||||
// console.log('123', val)
|
||||
init(val);
|
||||
}
|
||||
}, {immediate: true});
|
||||
// update-begin--author:liaozhiyang---date:20240603---for:【TV360X-342】字段下拉样式调整
|
||||
const getTreePopupClass = computed(() => {
|
||||
const findItem = fieldTreeData.value.find((item) => item.children);
|
||||
return findItem ? `${treePopupClass} containTable` : `${treePopupClass} noTable`;
|
||||
});
|
||||
// update-end--author:liaozhiyang---date:20240603---for:【TV360X-342】字段下拉样式调整
|
||||
|
||||
return {
|
||||
formRef,
|
||||
registerFormModal,
|
||||
init,
|
||||
handleChangeField,
|
||||
dynamicRowValues,
|
||||
matchType,
|
||||
historyCollapsed,
|
||||
registerModal,
|
||||
handleSubmit,
|
||||
handleCancel,
|
||||
handleSave,
|
||||
handleReset,
|
||||
doSaveQueryInfo,
|
||||
saveInfo,
|
||||
saveTreeData,
|
||||
handleTreeSelect,
|
||||
handleRemoveSaveInfo,
|
||||
fieldTreeData,
|
||||
addOne,
|
||||
removeOne,
|
||||
setFormModel,
|
||||
getSchema,
|
||||
loading,
|
||||
onFinish,
|
||||
getPopupContainer,
|
||||
superQueryFlag,
|
||||
handleOpen,
|
||||
initDefaultValues,
|
||||
simpleImage: Empty.PRESENTED_IMAGE_SIMPLE,
|
||||
saveModalLoading,
|
||||
queryFormStyle,
|
||||
handleFullScreen,
|
||||
fieldTreeSelectHeight,
|
||||
getTreePopupClass,
|
||||
handleStop,
|
||||
getQueryCondition,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.j-super-query-modal {
|
||||
.scrollbar__wrap {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="less">
|
||||
|
||||
:deep(.jee-super-query-form) {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
min-height: 300px;
|
||||
.ant-form-item{
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.j-super-query-history-tree) {
|
||||
.ant-tree-switcher{
|
||||
width: 0px;
|
||||
}
|
||||
.ant-tree-node-content-wrapper{
|
||||
width:100%;
|
||||
&:hover{
|
||||
background-color: #e6f7ff !important;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
.ant-tree-treenode-switcher-close{
|
||||
.ant-tree-title{
|
||||
display: inline-block;
|
||||
width: calc(100% - 30px);
|
||||
>div{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.icon-cancle{
|
||||
display: none;
|
||||
color: #666666;
|
||||
&:hover{
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.icon-cancle{
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-card-body{
|
||||
padding: 0;
|
||||
}
|
||||
// 保存查询样式宽度调整
|
||||
.ant-tree-treenode { width: 100%;}
|
||||
}
|
||||
|
||||
.j-super-query-history-card {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
//【QQYUN-7526】两行移入时会挤成三行
|
||||
width: 180px;
|
||||
min-height: 200px;
|
||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.2);
|
||||
|
||||
|
||||
&.collapsed {
|
||||
//【QQYUN-7526】两行移入时会挤成三行
|
||||
right: -180px;
|
||||
}
|
||||
|
||||
.collapse-box {
|
||||
position: absolute;
|
||||
top: calc(50% - 15px);
|
||||
left: -20px;
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-left: none;
|
||||
border-right-color: #ffffff;
|
||||
border-radius: 15px 0 0 15px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-shadow: -4px 0 6px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.app-iconify {
|
||||
font-size: 16px;
|
||||
position: relative;
|
||||
right: -3px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&.collapsed .app-iconify{
|
||||
right: 0;
|
||||
}
|
||||
|
||||
:deep(.ant-card-body),
|
||||
:deep(.ant-card-head){
|
||||
padding: 8px;
|
||||
min-height: 43px;
|
||||
.ant-card-head-title {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*VUEN-1087 【移动端】高级查询显示不全 */
|
||||
@media only screen and(max-width: 1050px) {
|
||||
:deep(.jee-super-query-form){
|
||||
.ant-space{
|
||||
flex-direction:column;
|
||||
gap: 0 !important;
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
.ant-space-item{
|
||||
width: 100%;
|
||||
}
|
||||
.ant-form-item{
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240524---for:【TV360X-524】高级查询增加放大功能
|
||||
.ant-form {
|
||||
& > .ant-space {
|
||||
width: 100%;
|
||||
:deep(& > .ant-space-item) {
|
||||
&:nth-child(1) {
|
||||
width: 20%;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
width: 15%;
|
||||
}
|
||||
&:nth-child(3) {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240524---for:【TV360X-524】高级查询增加放大功能
|
||||
|
||||
.containTable {
|
||||
:deep(.ant-select-tree-treenode) {
|
||||
> .ant-select-tree-switcher { display: none;}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
@tree-popup: ~'@{namespace}-super-query-tree-popup';
|
||||
.@{tree-popup} {
|
||||
// update-begin--author:liaozhiyang---date:20240603---for:【TV360X-342】字段下拉样式调整
|
||||
&.noTable {
|
||||
.ant-select-tree-treenode {
|
||||
> .ant-select-tree-switcher {
|
||||
display: none;
|
||||
}
|
||||
.ant-select-tree-node-content-wrapper {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
.ant-select-tree-title {
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240603---for:【TV360X-342】字段下拉样式调整
|
||||
// update-end--author:liaozhiyang---date:20240612---for:【TV360X-1005】有子表时结构化主表且超长省略
|
||||
&.containTable {
|
||||
.ant-select-tree-indent-unit {
|
||||
display: none;
|
||||
}
|
||||
.ant-select-tree-node-content-wrapper {
|
||||
display: block;
|
||||
max-width: calc(100% - 24px);
|
||||
}
|
||||
.ant-select-tree-title {
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240612---for:【TV360X-1005】有子表时结构化主表且超长省略
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,108 @@
|
||||
<script lang="tsx">
|
||||
import { computed, defineComponent, PropType, unref } from 'vue';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import { upperFirst } from 'lodash-es';
|
||||
import { componentMap } from '/@/components/Form/src/componentMap';
|
||||
import { createPlaceholderMessage } from '/@/components/Form/src/helper';
|
||||
import { isFunction } from '/@/utils/is';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SuperQueryValComponent',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
schema: {
|
||||
type: Object as PropType<FormSchema>,
|
||||
default: () => ({}),
|
||||
},
|
||||
formModel: {
|
||||
type: Object as PropType<Recordable>,
|
||||
default: () => ({}),
|
||||
},
|
||||
setFormModel: {
|
||||
type: Function as PropType<(key: string, value: any) => void>,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
emits: ['submit'],
|
||||
setup(props, { emit }) {
|
||||
const getComponentsProps = computed(() => {
|
||||
const {schema, formModel} = props;
|
||||
let {componentProps = {}} = schema;
|
||||
if (isFunction(componentProps)) {
|
||||
componentProps = componentProps({schema, formModel}) ?? {};
|
||||
}
|
||||
return componentProps as Recordable;
|
||||
});
|
||||
|
||||
const getValues = computed(() => {
|
||||
const {formModel, schema} = props;
|
||||
let obj = {
|
||||
field: schema.field,
|
||||
model: formModel,
|
||||
values: {
|
||||
...formModel,
|
||||
} as Recordable,
|
||||
schema: schema,
|
||||
};
|
||||
return obj
|
||||
});
|
||||
|
||||
function renderComponent() {
|
||||
const {
|
||||
component,
|
||||
changeEvent = 'change',
|
||||
valueField,
|
||||
} = props.schema;
|
||||
const field = 'val';
|
||||
const isCheck = component && ['Switch', 'Checkbox'].includes(component);
|
||||
const eventKey = `on${upperFirst(changeEvent)}`;
|
||||
const on = {
|
||||
[eventKey]: (...args: Nullable<Recordable>[]) => {
|
||||
const [e] = args;
|
||||
if (propsData[eventKey]) {
|
||||
propsData[eventKey](...args);
|
||||
}
|
||||
const target = e ? e.target : null;
|
||||
const value = target ? (isCheck ? target.checked : target.value) : e;
|
||||
props.setFormModel(field, value);
|
||||
},
|
||||
};
|
||||
const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
|
||||
|
||||
const propsData: Recordable = {
|
||||
allowClear: true,
|
||||
getPopupContainer: (trigger: Element) => trigger?.parentNode,
|
||||
...unref(getComponentsProps)
|
||||
};
|
||||
|
||||
const isCreatePlaceholder = !propsData.disabled;
|
||||
|
||||
// RangePicker place是一个数组
|
||||
if (isCreatePlaceholder && component !== 'RangePicker' && component) {
|
||||
//自动设置placeholder
|
||||
propsData.placeholder =
|
||||
unref(getComponentsProps)?.placeholder ||
|
||||
createPlaceholderMessage(component) + props.schema.label;
|
||||
}
|
||||
propsData.codeField = field;
|
||||
propsData.formValues = unref(getValues);
|
||||
const bindValue: Recordable = {
|
||||
[valueField || (isCheck ? 'checked' : 'value')]: props.formModel[field],
|
||||
};
|
||||
const compAttr: Recordable = {
|
||||
...propsData,
|
||||
...on,
|
||||
...bindValue,
|
||||
allowClear: true,
|
||||
onPressEnter() {
|
||||
emit('submit');
|
||||
},
|
||||
};
|
||||
return <Comp {...compAttr} />;
|
||||
}
|
||||
return ()=>{
|
||||
return (<div style="width:100%">{renderComponent()}</div>)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,703 @@
|
||||
import { useModalInner } from '/@/components/Modal';
|
||||
import { randomString } from '/@/utils/common/compUtils';
|
||||
import { reactive, ref, toRaw, watch } from 'vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { createLocalStorage } from '/@/utils/cache';
|
||||
import { useRoute } from 'vue-router';
|
||||
import FormSchemaFactory from '../factory/FormSchemaFactory';
|
||||
import {useExtendComponent} from '../../../hooks/auto/useExtendComponent'
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
/**
|
||||
* 表单类型转换成查询类型
|
||||
* 普通查询和高级查询组件区别 :高级查询不支持联动组件
|
||||
*/
|
||||
const FORM_VIEW_TO_QUERY_VIEW = {
|
||||
"password": "text",
|
||||
"file": "text",
|
||||
"image": "text",
|
||||
"textarea": "text",
|
||||
"umeditor": "text",
|
||||
"markdown": "text",
|
||||
"checkbox": "list_multi",
|
||||
"radio": "list",
|
||||
}
|
||||
|
||||
// 查询条件存储编码前缀
|
||||
const SAVE_CODE_PRE = 'JSuperQuerySaved_';
|
||||
|
||||
/**
|
||||
* 查询项
|
||||
* */
|
||||
interface SuperQueryItem {
|
||||
field: string|undefined;
|
||||
rule: string|undefined;
|
||||
val: string|number;
|
||||
key: string;
|
||||
// 解决inputNumber组件对不齐样式问题
|
||||
curLineAlign: string | undefined;
|
||||
fileType: string;
|
||||
// update-begin--author:liaozhiyang---date:20240611---for:【TV360X-461】字段类型是string,控件是text,则默认模糊查询
|
||||
view: string;
|
||||
// 最先原始的组件类型;view字段可能会被改变
|
||||
originView?: string;
|
||||
// update-end--author:liaozhiyang---date:20240611---for:【TV360X-461】字段类型是string,控件是text,则默认模糊查询
|
||||
}
|
||||
/**
|
||||
* 查询项-第一个控件树model
|
||||
* */
|
||||
interface TreeModel {
|
||||
title: string,
|
||||
value: string,
|
||||
isLeaf?: boolean,
|
||||
disabled?: boolean,
|
||||
children?: TreeModel[],
|
||||
order?: number,
|
||||
fieldType?: string;
|
||||
// update-begin--author:liaozhiyang---date:20240611---for:【TV360X-461】字段类型是string,控件是text,则默认模糊查询
|
||||
view: string;
|
||||
originView?: string;
|
||||
// update-end--author:liaozhiyang---date:20240611---for:【TV360X-461】字段类型是string,控件是text,则默认模糊查询
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询信息保存结构
|
||||
* */
|
||||
interface SaveModel{
|
||||
title: string,
|
||||
content: string,
|
||||
type: string,
|
||||
}
|
||||
|
||||
export function useSuperQuery(props){
|
||||
// 添加表单组件
|
||||
const {linkTableCard2Select} = useExtendComponent();
|
||||
|
||||
const { createMessage: $message } = useMessage();
|
||||
/** 表单ref*/
|
||||
const formRef = ref<any>();
|
||||
|
||||
/** 数据*/
|
||||
const dynamicRowValues = reactive<{ values: SuperQueryItem[] }>({
|
||||
values: [],
|
||||
});
|
||||
/** and/or */
|
||||
const matchType = ref('and');
|
||||
|
||||
// 保存查询弹窗确定按钮loading状态
|
||||
const saveModalLoading = ref(false);
|
||||
// 弹框显示
|
||||
const [registerModal, {setModalProps}] = useModalInner(() => {
|
||||
setModalProps({confirmLoading: false});
|
||||
})
|
||||
|
||||
// 高级查询类型不支持联动组件,需要额外设置联动组件的view为text
|
||||
const view2QueryViewMap = Object.assign({}, {"link_down":"text"}, FORM_VIEW_TO_QUERY_VIEW)
|
||||
|
||||
/**
|
||||
* 确认按钮事件
|
||||
*/
|
||||
function handleSubmit() {
|
||||
console.log('handleSubmit', dynamicRowValues.values)
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭按钮事件
|
||||
*/
|
||||
function handleCancel() {
|
||||
//closeModal();
|
||||
}
|
||||
|
||||
/**
|
||||
* val组件赋值
|
||||
*/
|
||||
function setFormModel(key: string, value: any, item: any) {
|
||||
console.log('setFormModel', key, value)
|
||||
// formModel[key] = value;
|
||||
item['val'] = value;
|
||||
}
|
||||
|
||||
// 字段-Properties
|
||||
const fieldProperties = ref<any>({})
|
||||
// 字段-左侧查询项-树控件数据
|
||||
const fieldTreeData = ref<any>([])
|
||||
// update-begin--author:liaozhiyang---date:20240607---for:【TV360X-503】过滤图片,文件、密码组件
|
||||
const filterComponent = (data) => {
|
||||
const { properties = {} } = data;
|
||||
Object.entries(properties).forEach(([field, value]) => {
|
||||
if (value.view === 'table') {
|
||||
filterComponent(value);
|
||||
}
|
||||
if (['link_down'].includes(value.originView || value.view)) {
|
||||
delete properties[field];
|
||||
}
|
||||
});
|
||||
};
|
||||
// update-end--author:liaozhiyang---date:20240607---for:【TV360X-503】过滤图片,文件、密码组件
|
||||
/**
|
||||
* 初始化数据-最开始的方法
|
||||
* 1.获取 表名@字段名-->配置 这样的一个map
|
||||
* 2.获取树形结构的数据 显示:文本; 存储:表名@字段名
|
||||
* 当树改变时,及时获取配置更新表单
|
||||
* @param json
|
||||
*/
|
||||
function init(json) {
|
||||
console.log('=============')
|
||||
console.log('=============', json)
|
||||
console.log('=============')
|
||||
// update-begin--author:liaozhiyang---date:20240607---for:【TV360X-503】过滤图片,文件、密码组件
|
||||
filterComponent(json);
|
||||
// update-end--author:liaozhiyang---date:20240607---for:【TV360X-503】过滤图片,文件、密码组件
|
||||
let { allFields, treeData } = getAllFields(json);
|
||||
fieldProperties.value = allFields;
|
||||
// update-end--author:liaozhiyang---date:20240612---for:【TV360X-1005】有子表时结构化主表且超长省略
|
||||
const properties = json.properties ?? {};
|
||||
const subTable: string[] = [];
|
||||
const tableName = json.table;
|
||||
Object.entries(properties).forEach(([key, value]: [string, any]) => {
|
||||
if (value.view === 'table') {
|
||||
subTable.push(key);
|
||||
}
|
||||
});
|
||||
if (subTable.length) {
|
||||
let arr: TreeModel[] = [];
|
||||
arr = treeData.filter((item) => !subTable.includes(item.value));
|
||||
for (let i = 0, len = treeData.length; i < len; i++) {
|
||||
const item = treeData[i];
|
||||
if (!subTable.includes(item.value)) {
|
||||
treeData.splice(i, 1);
|
||||
i--;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
treeData.unshift({ title: '主表', value: tableName, disabled: true, order: 200, children: arr, view: 'table' });
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240612---for:【TV360X-1005】有子表时结构化主表且超长省略
|
||||
fieldTreeData.value = treeData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 左侧查询项 添加一行
|
||||
* @param index
|
||||
*/
|
||||
function addOne(index) {
|
||||
let item = {
|
||||
field: undefined,
|
||||
rule: 'eq',
|
||||
val:'',
|
||||
key: randomString(16)
|
||||
}
|
||||
if(index===false){
|
||||
// 重置后需要调用
|
||||
dynamicRowValues.values = []
|
||||
dynamicRowValues.values.push(item)
|
||||
}else if(index===true){
|
||||
// 打开弹框是需要调用
|
||||
if(dynamicRowValues.values.length==0){
|
||||
dynamicRowValues.values.push(item)
|
||||
}
|
||||
}else{
|
||||
// 其余就是 正常的点击加号增加行
|
||||
dynamicRowValues.values.splice(++index, 0, item)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 左侧查询项 删除一行
|
||||
*/
|
||||
function removeOne(item: SuperQueryItem) {
|
||||
let arr = toRaw(dynamicRowValues.values);
|
||||
let index = -1;
|
||||
for(let i=0;i<arr.length;i++){
|
||||
if(item.key == arr[i].key){
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(index != -1){
|
||||
dynamicRowValues.values.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 默认的输入框
|
||||
const defaultInput = {
|
||||
field: "val",
|
||||
label: "测试",
|
||||
component:"Input"
|
||||
}
|
||||
|
||||
/**
|
||||
* 左侧查询项 val组件 schema获取, 替代左侧字段树的change事件
|
||||
* @param item
|
||||
* @param index
|
||||
*/
|
||||
function getSchema(item, index) {
|
||||
let map = fieldProperties.value
|
||||
let prop = map[item.field]
|
||||
if(!prop){
|
||||
return defaultInput
|
||||
}
|
||||
if(view2QueryViewMap[prop.view]){
|
||||
// 如果出现查询条件联动组件出来的场景,请跟踪此处
|
||||
prop.view = view2QueryViewMap[prop.view]
|
||||
}
|
||||
let temp = FormSchemaFactory.createFormSchema(item.field, prop)
|
||||
// temp.setFormRef(formRef)
|
||||
temp.noChange()
|
||||
// 查询条件中的 下拉框popContainer为parentNode
|
||||
temp.asSearchForm();
|
||||
temp.updateField(item.field+index)
|
||||
const setFieldValue = (values)=>{
|
||||
item['val'] = values[item.field]
|
||||
}
|
||||
temp.setFunctionForFieldValue(setFieldValue)
|
||||
let schema = temp.getFormItemSchema()
|
||||
//schema['valueField'] = 'val'
|
||||
// 特殊规则,需要禁用组件
|
||||
// 为空、不为空
|
||||
if (['empty', 'not_empty'].includes(item.rule)) {
|
||||
schema.componentProps = { ...schema.componentProps, disabled: true };
|
||||
}
|
||||
linkTableCard2Select(schema);
|
||||
// update-begin--author:liaozhiyang---date:20240607---for:【TV360X-389】普通查询关联记录去掉编辑按钮
|
||||
if (schema.component === 'LinkTableSelect') {
|
||||
let componentProps = schema.componentProps ?? {};
|
||||
schema.componentProps = { ...componentProps, editBtnShow: false };
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240607---for:【TV360X-389】普通查询关联记录去掉编辑按钮
|
||||
// update-begin--author:liaozhiyang---date:20231219---for:【QQYUN-7640】高级查询数字组件会偏移
|
||||
if (schema && schema.component === 'InputNumber') {
|
||||
item.curLineAlign = 'start';
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20231219---for:【QQYUN-7640】高级查询数字组件会偏移
|
||||
// update-begin--author:liaozhiyang---date:20240223---for:【QQYUN-8229】高级选择自定义树下拉显示不全
|
||||
if (schema?.component === 'JTreeSelect') {
|
||||
let componentProps: any = schema.componentProps;
|
||||
if (componentProps) {
|
||||
componentProps.getPopupContainer = () => document.body
|
||||
} else {
|
||||
schema.componentProps = { getPopupContainer: () => document.body }
|
||||
};
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240223---for:【QQYUN-8229】高级选择自定义树下拉显示不全
|
||||
// update-begin--author:liaozhiyang---date:20240529---for:【TV360X-499】高级查询开关组件换成下拉,用户组件不显示按钮
|
||||
if (schema?.component === 'JSwitch') {
|
||||
const componentProps = schema.componentProps ?? {};
|
||||
schema.componentProps = { ...componentProps, query: true };
|
||||
}
|
||||
if (schema?.component === 'JSelectUser') {
|
||||
const componentProps = schema.componentProps ?? {};
|
||||
schema.componentProps = { ...componentProps, showButton: false };
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240529---for:【TV360X-499】高级查询开关换成下拉,用户组件不显示按钮
|
||||
return schema
|
||||
}
|
||||
|
||||
/*-----------------------右侧保存信息相关-begin---------------------------*/
|
||||
|
||||
/**
|
||||
* 右侧树 的 数据
|
||||
*/
|
||||
const saveTreeData = ref<any>('')
|
||||
// 本地缓存
|
||||
const $ls = createLocalStorage();
|
||||
//需要保存的信息(一条)
|
||||
const saveInfo = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
content: '',
|
||||
saveCode: ''
|
||||
});
|
||||
//按钮loading
|
||||
const loading = ref(false)
|
||||
|
||||
// 当前页面路由
|
||||
const route = useRoute();
|
||||
// update-begin--author:liaozhiyang---date:20240514---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
if (props.isCustomSave) {
|
||||
watch(props.saveSearchData, () => {
|
||||
currentPageSavedArray.value = props.saveSearchData;
|
||||
});
|
||||
} else {
|
||||
// 监听路由信息,路由发生改变,则重新获取保存的查询信息-->currentPageSavedArray
|
||||
watch(()=>route.fullPath, (val)=>{
|
||||
console.log('fullpath', val);
|
||||
initSaveQueryInfoCode();
|
||||
});
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240514---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
|
||||
// 当前页面存储的 查询信息
|
||||
const currentPageSavedArray = ref<SaveModel[]>([]);
|
||||
// 监听当前页面是否有新的数据保存了,然后更新右侧数据->saveTreeData
|
||||
watch(()=>currentPageSavedArray.value, (val)=>{
|
||||
let temp:any[] = []
|
||||
if(val && val.length>0){
|
||||
val.map(item=>{
|
||||
let key = randomString(16)
|
||||
temp.push({
|
||||
title: item.title,
|
||||
slots: { icon: 'custom' },
|
||||
value: key
|
||||
})
|
||||
})
|
||||
}
|
||||
saveTreeData.value = temp
|
||||
}, {immediate:true, deep: true})
|
||||
|
||||
|
||||
// 重新获取保存的查询信息
|
||||
function initSaveQueryInfoCode(){
|
||||
// update-begin--author:liaozhiyang---date:20240514---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
if (props.isCustomSave) {
|
||||
currentPageSavedArray.value = cloneDeep(props.saveSearchData);
|
||||
} else {
|
||||
let code = SAVE_CODE_PRE + route.fullPath;
|
||||
saveInfo.saveCode = code;
|
||||
let list = $ls.get(code);
|
||||
if(list && list instanceof Array){
|
||||
currentPageSavedArray.value = list
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240514---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
}
|
||||
|
||||
// 执行一次 获取保存的查询信息
|
||||
initSaveQueryInfoCode();
|
||||
|
||||
/**
|
||||
* 保存按钮事件
|
||||
*/
|
||||
function handleSave(){
|
||||
// 获取实际数据转成字符串
|
||||
let fieldArray = getQueryInfo();
|
||||
if(!fieldArray){
|
||||
$message.warning('空条件不能保存')
|
||||
return;
|
||||
}
|
||||
let content = JSON.stringify(fieldArray)
|
||||
openSaveInfoModal(content)
|
||||
}
|
||||
|
||||
// 输入保存标题 弹框显示
|
||||
function openSaveInfoModal(content){
|
||||
saveInfo.visible = true;
|
||||
saveInfo.title = '';
|
||||
saveInfo.content = content
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认保存查询信息
|
||||
*/
|
||||
function doSaveQueryInfo(){
|
||||
let { title, content, saveCode } = saveInfo;
|
||||
let index = getTitleIndex(title);
|
||||
const saveSearchCondition = (type) => {
|
||||
// update-begin--author:liaozhiyang---date:20240514---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
const curPageSave: any = cloneDeep(currentPageSavedArray.value);
|
||||
saveModalLoading.value = true;
|
||||
if (type) {
|
||||
// 覆盖已有
|
||||
curPageSave.splice(index, 1, {
|
||||
content,
|
||||
title,
|
||||
type: matchType.value,
|
||||
});
|
||||
} else {
|
||||
curPageSave.push({
|
||||
content,
|
||||
title,
|
||||
type: matchType.value,
|
||||
});
|
||||
}
|
||||
const run = () => {
|
||||
saveInfo.visible = false;
|
||||
$message.success('保存成功');
|
||||
currentPageSavedArray.value = curPageSave;
|
||||
saveModalLoading.value = false;
|
||||
};
|
||||
if (props.isCustomSave) {
|
||||
props
|
||||
.save(curPageSave, 0)
|
||||
.then(() => {
|
||||
run();
|
||||
})
|
||||
.catch((err) => {
|
||||
saveModalLoading.value = false;
|
||||
});
|
||||
} else {
|
||||
// update-begin--author:liaozhiyang---date:20240306---for:【QQYUN-8357】高级查询保存的查询条件缓存改成一个月
|
||||
const expire = 60 * 60 * 24 * 30;
|
||||
// update-end--author:liaozhiyang---date:20240306---for:【QQYUN-8357】高级查询保存的查询条件缓存改成一个月
|
||||
$ls.set(saveCode, curPageSave, expire);
|
||||
run();
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240514---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
};
|
||||
if (index >= 0) {
|
||||
// 已存在是否覆盖
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: `${title} 已存在,是否覆盖?`,
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
saveSearchCondition(1);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
saveSearchCondition(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据填入的 title找本地存储的信息,如果有需要询问是否覆盖
|
||||
function getTitleIndex(title){
|
||||
let savedArray = currentPageSavedArray.value
|
||||
let index = -1;
|
||||
for(let i=0;i<savedArray.length;i++){
|
||||
if(savedArray[i].title == title){
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取左侧所有查询条件,如果没有/或者条件无效则返回false
|
||||
*/
|
||||
function getQueryInfo(isEmit = false){
|
||||
let arr = dynamicRowValues.values;
|
||||
if(!arr || arr.length==0){
|
||||
return false;
|
||||
}
|
||||
let fieldArray:any = [];
|
||||
let fieldProps = fieldProperties.value;
|
||||
for(let item of arr){
|
||||
let allowEmpty = ['empty', 'not_empty'].includes(item.rule!)
|
||||
if(item.field && (allowEmpty || item.val || item.val===0) && item.rule){
|
||||
let prop = fieldProps[item.field]
|
||||
// 自定义格式化数据
|
||||
let formatValue = prop?.formatValue ?? (v => v)
|
||||
let tempVal:any = toRaw(item.val)
|
||||
if(tempVal instanceof Array){
|
||||
tempVal = tempVal.map(v => formatValue(v)).join(',')
|
||||
} else {
|
||||
tempVal = formatValue(tempVal)
|
||||
}
|
||||
let fieldName = getRealFieldName(item)
|
||||
let obj = {
|
||||
field: fieldName,
|
||||
rule: item.rule,
|
||||
val: tempVal,
|
||||
fileType: item.fileType ,
|
||||
};
|
||||
if(isEmit===true){
|
||||
//如果当前数据用于emit事件,需要设置dbtype和type
|
||||
let prop = fieldProps[item.field]
|
||||
if(prop){
|
||||
obj['type'] = prop.view
|
||||
obj['dbType'] = prop.type
|
||||
}
|
||||
}
|
||||
fieldArray.push(obj)
|
||||
}
|
||||
}
|
||||
if(fieldArray.length==0){
|
||||
return false;
|
||||
}
|
||||
return fieldArray
|
||||
}
|
||||
|
||||
//update-begin-author:taoyan date:2022-5-31 for: VUEN-1148 主子联动下,高级查询查子表数据,无效
|
||||
/**
|
||||
* 高级查询参数 字段名
|
||||
* 获取后台需要的 字段名格式:表名,字段名
|
||||
* @param item
|
||||
*/
|
||||
function getRealFieldName(item){
|
||||
let fieldName = item.field
|
||||
if(fieldName.indexOf('@')>0){
|
||||
fieldName = fieldName.replace('@', ',')
|
||||
}
|
||||
return fieldName;
|
||||
}
|
||||
//update-end-author:taoyan date:2022-5-31 for: VUEN-1148 主子联动下,高级查询查子表数据,无效
|
||||
|
||||
/**
|
||||
* 右侧数据 点击事件,重新将数据显示到左侧
|
||||
* @param key
|
||||
* @param node
|
||||
*/
|
||||
function handleTreeSelect(key, {node}){
|
||||
console.log(key, node)
|
||||
let title = node.dataRef.title
|
||||
let arr = currentPageSavedArray.value.filter(item=>item.title==title)
|
||||
if(arr && arr.length>0){
|
||||
// 拿到数据渲染
|
||||
let { content, type } = arr[0]
|
||||
let data = JSON.parse(content)
|
||||
let rowsValues: SuperQueryItem[] = []
|
||||
for(let item of data){
|
||||
// update-begin--author:liaozhiyang---date:20240108---for:【issues/962】高级查询保存的查询是子表,下次查询不出结果
|
||||
item.field = item.field.replace(',','@');
|
||||
// update-end--author:liaozhiyang---date:20240108---for:【issues/962】高级查询保存的查询是子表,下次查询不出结果
|
||||
rowsValues.push(Object.assign({}, {key: randomString(16)}, item))
|
||||
}
|
||||
dynamicRowValues.values = rowsValues
|
||||
matchType.value = type
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 右侧数据 删除事件
|
||||
*/
|
||||
function handleRemoveSaveInfo(title){
|
||||
console.log(title)
|
||||
let index = getTitleIndex(title)
|
||||
if(index>=0){
|
||||
// update-begin--author:liaozhiyang---date:20240513---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
if (props.isCustomSave) {
|
||||
const curPageSave = cloneDeep(currentPageSavedArray.value);
|
||||
curPageSave.splice(index, 1);
|
||||
props
|
||||
.save(curPageSave, 1)
|
||||
.then(() => {
|
||||
currentPageSavedArray.value = curPageSave;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(`删除是吧~,${err}`);
|
||||
});
|
||||
} else {
|
||||
currentPageSavedArray.value.splice(index, 1);
|
||||
$ls.set(saveInfo.saveCode, currentPageSavedArray.value);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240513---for:【issues/6205】高级查询组件增加保存条件自定义存储方式
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------右侧保存信息相关-end---------------------------*/
|
||||
|
||||
// 获取所有字段配置信息
|
||||
function getAllFields(properties){
|
||||
// 获取所有配置 查询字段 是否联合查询
|
||||
// const {properties, table, title } = json;
|
||||
let allFields = {}
|
||||
let order = 1;
|
||||
let treeData:TreeModel[] = []
|
||||
/* let mainNode:TreeModel = {
|
||||
title,
|
||||
value: table,
|
||||
disabled: true,
|
||||
children: []
|
||||
};*/
|
||||
//treeData.push(mainNode)
|
||||
if(properties.properties){
|
||||
properties = properties.properties
|
||||
}
|
||||
Object.keys(properties).map(field=>{
|
||||
let item = properties[field]
|
||||
if(item.view == 'table'){
|
||||
// 子表字段
|
||||
// 联合查询开启才需要子表字段作为查询条件
|
||||
let subProps = item['properties'] || item['fields']
|
||||
let subTableOrder = order * 100;
|
||||
let subNode:TreeModel = {
|
||||
title: item.title,
|
||||
value: field,
|
||||
disabled: true,
|
||||
children: [],
|
||||
order: subTableOrder,
|
||||
// update-begin--author:liaozhiyang---date:20240306---for:【TV360X-461】字段类型是string,则默认模糊查询
|
||||
fieldType: item.type,
|
||||
// update-end--author:liaozhiyang---date:20240306---for:【TV360X-461】字段类型是string,则默认模糊查询
|
||||
}
|
||||
Object.keys(subProps).map(subField=>{
|
||||
let subItem = subProps[subField];
|
||||
// 保证排序统一
|
||||
subItem['order'] = subTableOrder + subItem['order']
|
||||
let subFieldKey = field+'@'+subField
|
||||
allFields[subFieldKey] = subItem
|
||||
subNode.children!.push({
|
||||
title: subItem.title,
|
||||
value: subFieldKey,
|
||||
isLeaf: true,
|
||||
order: subItem['order'],
|
||||
// update-begin--author:liaozhiyang---date:20240306---for:【TV360X-461】字段类型是string,则默认模糊查询
|
||||
fieldType: subItem.type,
|
||||
view: subItem.view,
|
||||
originView: subItem.view,
|
||||
// update-end--author:liaozhiyang---date:20240306---for:【TV360X-461】字段类型是string,则默认模糊查询
|
||||
})
|
||||
});
|
||||
orderField(subNode);
|
||||
treeData.push(subNode);
|
||||
order++;
|
||||
}else{
|
||||
// 主表字段
|
||||
//let fieldKey = table+'@'+field
|
||||
let fieldKey = field
|
||||
allFields[fieldKey] = item
|
||||
treeData.push({
|
||||
title: item.title,
|
||||
value: fieldKey,
|
||||
isLeaf: true,
|
||||
order: item.order,
|
||||
// update-begin--author:liaozhiyang---date:20240306---for:【TV360X-461】字段类型是string,则默认模糊查询
|
||||
fieldType: item.type,
|
||||
view: item.view,
|
||||
originView: item.view,
|
||||
// update-end--author:liaozhiyang---date:20240306---for:【TV360X-461】字段类型是string,则默认模糊查询
|
||||
});
|
||||
}
|
||||
});
|
||||
orderField(treeData);
|
||||
return {allFields, treeData}
|
||||
}
|
||||
|
||||
//根据字段的order重新排序
|
||||
function orderField(data){
|
||||
let arr = data.children || data;
|
||||
arr.sort(function (a, b) {
|
||||
return a.order - b.order
|
||||
});
|
||||
}
|
||||
|
||||
function initDefaultValues(values) {
|
||||
const { params, matchType } = values
|
||||
if(params){
|
||||
let rowsValues: SuperQueryItem[] = []
|
||||
for(let item of params){
|
||||
rowsValues.push(Object.assign({}, {key: randomString(16)}, item))
|
||||
}
|
||||
dynamicRowValues.values = rowsValues
|
||||
matchType.value = matchType
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
formRef,
|
||||
init,
|
||||
dynamicRowValues,
|
||||
matchType,
|
||||
registerModal,
|
||||
handleSubmit,
|
||||
handleCancel,
|
||||
handleSave,
|
||||
doSaveQueryInfo,
|
||||
saveInfo,
|
||||
saveTreeData,
|
||||
handleRemoveSaveInfo,
|
||||
handleTreeSelect,
|
||||
fieldTreeData,
|
||||
addOne,
|
||||
removeOne,
|
||||
setFormModel,
|
||||
getSchema,
|
||||
loading,
|
||||
getQueryInfo,
|
||||
initDefaultValues,
|
||||
saveModalLoading,
|
||||
fieldProperties,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,532 @@
|
||||
<template>
|
||||
<div :class="['p-2', `online-list-${ID}`]">
|
||||
|
||||
<!-- 加载中骨架屏 -->
|
||||
<a-skeleton v-if="tableReloading" active />
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<online-query-form
|
||||
v-show="!tableReloading"
|
||||
ref="onlineQueryFormOuter"
|
||||
:id="ID"
|
||||
:queryBtnCfg="getQueryButtonCfg"
|
||||
:resetBtnCfg="getResetButtonCfg"
|
||||
@search="queryWithCondition"
|
||||
@loaded="onQueryFormLoaded"
|
||||
/>
|
||||
|
||||
<!-- 列表 -->
|
||||
<BasicTable
|
||||
v-if="!tableReloading"
|
||||
ref="onlineTable"
|
||||
rowKey="jeecg_row_key"
|
||||
:canResize="true"
|
||||
:bordered="true"
|
||||
:showIndexColumn="false"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="pagination"
|
||||
:rowSelection="rowSelection"
|
||||
:actionColumn="actionColumn"
|
||||
:showTableSetting="true"
|
||||
:clickToRowSelect="false"
|
||||
:scroll="tableScroll"
|
||||
@table-redo="reload"
|
||||
:class="{ 'j-table-force-nowrap': enableScrollBar }"
|
||||
@change="handleChangeInTable"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
v-if="buttonSwitch.add && cgBIBtnMap['add'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['add'].buttonIcon"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<span>{{cgBIBtnMap['add'].buttonName}}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="buttonSwitch.import && cgBIBtnMap['import'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['import'].buttonIcon"
|
||||
@click="onImportExcel"
|
||||
>
|
||||
<span>{{cgBIBtnMap['import'].buttonName}}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="buttonSwitch.export && cgBIBtnMap['export'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['export'].buttonIcon"
|
||||
:loading="exportLoading"
|
||||
@click="onExportExcelOverride"
|
||||
>
|
||||
<span>{{cgBIBtnMap['export'].buttonName}}</span>
|
||||
</a-button>
|
||||
<!-- update-begin--author:liaozhiyang---date:20250403---for:【QQYUN-11801】生成测试数据 -->
|
||||
<a-tooltip
|
||||
v-if="!isConfigCurRoute && (tableType == 1 || tableType == 2) && buttonSwitch.aigc_mock_data && cgBIBtnMap['aigc_mock_data']?.enabled && dataSource.length == 0 && !loading"
|
||||
:open="false"
|
||||
placement="bottom"
|
||||
>
|
||||
<template #title>
|
||||
<span>当有数据时或生成测试数据后该按钮会隐藏</span>
|
||||
</template>
|
||||
<a-button preIcon="mdi:robot-love-outline" :loading="testDataLoading" @click="handleAddTestData(currentTableName, reload)">
|
||||
{{ cgBIBtnMap['aigc_mock_data'].buttonName }}
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<!-- update-end--author:liaozhiyang---date:20250403---for:【QQYUN-11801】生成测试数据 -->
|
||||
<!-- 自定义按钮 -->
|
||||
<template v-if="cgTopButtonList && cgTopButtonList.length > 0" v-for="(item, index) in cgTopButtonList">
|
||||
<a-button
|
||||
v-if="item.optType == 'js'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonJsHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-else-if="item.optType == 'action'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonActionHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<a-button
|
||||
v-show="selectedKeys.length > 0"
|
||||
v-if="buttonSwitch.batch_delete && cgBIBtnMap['batch_delete'].enabled"
|
||||
:preIcon="cgBIBtnMap['batch_delete'].buttonIcon"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
<span>{{cgBIBtnMap['batch_delete'].buttonName}}</span>
|
||||
</a-button>
|
||||
|
||||
<online-super-query
|
||||
v-if="buttonSwitch.super_query && cgBIBtnMap['super_query'].enabled"
|
||||
ref="superQueryButtonRef"
|
||||
online
|
||||
:status="superQueryStatus"
|
||||
:queryBtnCfg="cgBIBtnMap['super_query']"
|
||||
@search="handleSuperQuery"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #fileSlot="{ text, record, column }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
|
||||
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download" size="small" @click="downloadRowFile(text, record, column, ID)">
|
||||
下载
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #imgSlot="{ text }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
|
||||
<img v-else :src="getImgView(text)" alt="图片不存在" class="online-cell-image" @click="viewOnlineCellImage(text)" />
|
||||
</template>
|
||||
|
||||
<template #htmlSlot="{ text, column, record }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
<template v-if="column.fieldHref">
|
||||
<a v-html="text" @click="handleClickFieldHref(column.fieldHref, record)"></a>
|
||||
</template>
|
||||
<div v-else v-html="text"></div>
|
||||
<!-- update-end--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
</template>
|
||||
|
||||
<template #pcaSlot="{ text, column }">
|
||||
<div :title="getPcaText(text, column)">{{ getPcaText(text, column) }}</div>
|
||||
</template>
|
||||
|
||||
<template #dateSlot="{ text, column }">
|
||||
<span>{{ getFormatDate(text, column) }}</span>
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 表单新增、修改弹框 -->
|
||||
<OnlineAutoModal
|
||||
@register="registerModal"
|
||||
:id="ID"
|
||||
:cgBIBtnMap="cgBIBtnMap"
|
||||
:buttonSwitch="buttonSwitch"
|
||||
:confirmBtnCfg="getFormConfirmButtonCfg"
|
||||
@success="reload"
|
||||
@formConfig="handleFormConfig"
|
||||
/>
|
||||
|
||||
<!-- 详情弹框 -->
|
||||
<online-detail-modal :id="ID" @register="registerDetailModal"/>
|
||||
|
||||
<!-- 导入 -->
|
||||
<JImportModal @register="registerImportModal" :url="importUrl()" @ok="reload" online></JImportModal>
|
||||
|
||||
<!-- 跳转Href的动态组件方式 -->
|
||||
<a-modal v-bind="hrefComponent.model" v-on="hrefComponent.on">
|
||||
<component :is="hrefComponent.is" v-bind="hrefComponent.params" />
|
||||
</a-modal>
|
||||
|
||||
<!-- 自定义弹窗 -->
|
||||
<online-custom-modal @register="registerCustomModal" @success="reload" />
|
||||
|
||||
<!-- 弹窗给href到另外一张表单用-详情表单 -->
|
||||
<online-detail-modal :id="hrefMainTableId" @register="registerOnlineHrefModal" :defaultFullscreen="false"/>
|
||||
|
||||
<!-- 弹窗到另外一张表单用-可编辑表单-关联记录的字段可在列表上打开modal编辑数据 -->
|
||||
<online-pop-modal ref="onlinePopModalRef" :id="popTableId" @register="registerPopModal" @success="reload" request topTip></online-pop-modal>
|
||||
|
||||
<!-- 流程图查看modal -->
|
||||
<BpmGraphicModal @register="registerBpmModal"></BpmGraphicModal>
|
||||
<!-- 页面loading[主要为了js增强使用] -->
|
||||
<Loading :loading="pageLoading" :absolute="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import OnlineAutoModal from './OnlineAutoModal.vue';
|
||||
import OnlineCustomModal from './OnlineCustomModal.vue';
|
||||
import OnlineDetailModal from './OnlineDetailModal.vue'
|
||||
import { watch } from 'vue';
|
||||
import JImportModal from '/@/components/Form/src/jeecg/components/JImportModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useTableColumns } from '../../hooks/auto/useTableColumns';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import OnlineQueryForm from '../comp/OnlineQueryForm.vue';
|
||||
import OnlineSuperQuery from '../comp/superquery/SuperQuery.vue';
|
||||
import { useOnlineListPopEvent } from '../../hooks/auto/useOnlinePopEvent';
|
||||
import OnlinePopModal from '../comp/OnlinePopModal.vue';
|
||||
import { NORMAL } from "../../util/constant";
|
||||
import { Loading } from '/@/components/Loading';
|
||||
|
||||
export default {
|
||||
name: 'OnlineAutoList',
|
||||
components: {
|
||||
BasicTable,
|
||||
TableAction,
|
||||
OnlineAutoModal,
|
||||
JImportModal,
|
||||
OnlineQueryForm,
|
||||
OnlineSuperQuery,
|
||||
OnlineCustomModal,
|
||||
OnlineDetailModal,
|
||||
OnlinePopModal,
|
||||
Loading,
|
||||
},
|
||||
setup() {
|
||||
const { createMessage: $message } = useMessage();
|
||||
const currentTableName = ref('');
|
||||
const tableType = ref('');
|
||||
// 这行代码应该在每次进入新的路由都会走,不管该路由有没有被缓存--
|
||||
const {
|
||||
ID,
|
||||
onlineTableContext,
|
||||
onlineQueryFormOuter,
|
||||
loading,
|
||||
reload,
|
||||
dataSource,
|
||||
pagination,
|
||||
handleSpecialConfig,
|
||||
getColumnList,
|
||||
handleChangeInTable,
|
||||
loadData,
|
||||
superQueryButtonRef,
|
||||
superQueryStatus,
|
||||
handleSuperQuery,
|
||||
onlineExtConfigJson,
|
||||
handleFormConfig,
|
||||
registerCustomModal,
|
||||
tableReloading,
|
||||
isConfigCurRoute,
|
||||
pageLoading,
|
||||
} = useOnlineTableContext();
|
||||
|
||||
// 判断 若ID不存在就终止后续逻辑
|
||||
if (!ID.value) {
|
||||
$message.warning('地址错误, 配置ID不存在!');
|
||||
// update-end--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
throw new Error('地址错误, 配置ID不存在!');
|
||||
// update-end--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
}
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
// 处理列表button
|
||||
const {
|
||||
buttonSwitch,
|
||||
cgLinkButtonList,
|
||||
cgBIBtnMap,
|
||||
getQueryButtonCfg,
|
||||
getResetButtonCfg,
|
||||
getFormConfirmButtonCfg,
|
||||
cgTopButtonList,
|
||||
importUrl,
|
||||
registerModal,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
handleBatchDelete,
|
||||
handleAddTestData,
|
||||
testDataLoading,
|
||||
testDataBtnShow,
|
||||
registerImportModal,
|
||||
onImportExcel,
|
||||
onExportExcel,
|
||||
cgButtonJsHandler,
|
||||
cgButtonActionHandler,
|
||||
cgButtonLinkHandler,
|
||||
handleSubmitFlow,
|
||||
getDropDownActions,
|
||||
getActions,
|
||||
initButtonList,
|
||||
initButtonSwitch,
|
||||
registerDetailModal,
|
||||
registerBpmModal
|
||||
} = useListButton(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
const exportLoading = ref(false)
|
||||
|
||||
// 重写导出方法,防止频繁点击
|
||||
async function onExportExcelOverride() {
|
||||
try {
|
||||
exportLoading.value = true
|
||||
await onExportExcel()
|
||||
} finally {
|
||||
// 防止频繁点击,延迟1.5s关闭loading
|
||||
setTimeout(() => exportLoading.value = false, 1500)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 BasicTable 的配置
|
||||
const {
|
||||
columns,
|
||||
actionColumn,
|
||||
selectedKeys,
|
||||
rowSelection,
|
||||
enableScrollBar,
|
||||
tableScroll,
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
handleColumnResult,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
hrefMainTableId,
|
||||
registerOnlineHrefModal,
|
||||
registerPopModal,
|
||||
openPopModal,
|
||||
onlinePopModalRef,
|
||||
popTableId,
|
||||
handleClickFieldHref,
|
||||
} = useTableColumns(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
// 监听表单配置ID
|
||||
watch(
|
||||
ID,
|
||||
() => {
|
||||
console.log('watched id is change...');
|
||||
initAutoList();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**重新加载online配置*/
|
||||
async function initAutoList() {
|
||||
loading.value = true;
|
||||
// 1.列配置信息
|
||||
let columnResult: any = await getColumnList(NORMAL);
|
||||
handleTableConfig(columnResult);
|
||||
// update-begin--author:liaozhiyang---date:20250403---for:【QQYUN-11801】生成测试数据
|
||||
currentTableName.value = columnResult.currentTableName;
|
||||
tableType.value = columnResult.tableType;
|
||||
// update-end--author:liaozhiyang---date:20250403---for:【QQYUN-11801】生成测试数据
|
||||
// 2.加载数据
|
||||
await loadData();
|
||||
loading.value = false;
|
||||
// 3.执行js增强 setup
|
||||
onlineTableContext.execButtonEnhance('setup');
|
||||
}
|
||||
|
||||
// 将查询结果转成 table渲染需要的配置
|
||||
function handleTableConfig(result) {
|
||||
// js增强初始化
|
||||
let EnhanceJS = initCgEnhanceJs(result.enhanceJs);
|
||||
onlineTableContext['EnhanceJS'] = EnhanceJS;
|
||||
// 自定义按钮设置
|
||||
initButtonList(result.cgButtonList);
|
||||
// 页面按钮显示隐藏状态设置
|
||||
initButtonSwitch(result.hideColumns);
|
||||
// 列配置
|
||||
handleColumnResult(result);
|
||||
// 表配置
|
||||
handleSpecialConfig(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询控件 事件-执行查询
|
||||
* @param data
|
||||
*/
|
||||
function queryWithCondition(data) {
|
||||
onlineTableContext['queryParam'] = data;
|
||||
reload({mode:'search'});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询组件加载完成事件,获取高级查询需要的字段信息
|
||||
*/
|
||||
async function onQueryFormLoaded(json) {
|
||||
console.log('onQueryFormLoaded', json)
|
||||
await getRefPromise(superQueryButtonRef)
|
||||
superQueryButtonRef.value.init(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* list页面打开 其他表单弹窗
|
||||
* @param params
|
||||
*/
|
||||
function openOnlinePopModal(params){
|
||||
console.log('openOnlinePopModal', params)
|
||||
popTableId.value = params.id;
|
||||
let data = {
|
||||
title: params.describe
|
||||
}
|
||||
if(params.record && params.record.id){
|
||||
data['record'] = params.record
|
||||
data['isUpdate'] = true;
|
||||
}
|
||||
openPopModal(true, data);
|
||||
}
|
||||
//绑定弹窗事件
|
||||
useOnlineListPopEvent(openOnlinePopModal);
|
||||
|
||||
const that = {
|
||||
ID,
|
||||
// 查询区域
|
||||
onlineQueryFormOuter,
|
||||
queryWithCondition,
|
||||
onQueryFormLoaded,
|
||||
reload,
|
||||
|
||||
//高级查询
|
||||
superQueryButtonRef,
|
||||
superQueryStatus,
|
||||
handleSuperQuery,
|
||||
|
||||
// table区域
|
||||
loading,
|
||||
columns,
|
||||
dataSource,
|
||||
pagination,
|
||||
actionColumn,
|
||||
rowSelection,
|
||||
selectedKeys,
|
||||
tableScroll,
|
||||
enableScrollBar,
|
||||
handleChangeInTable,
|
||||
|
||||
//按钮
|
||||
buttonSwitch,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
onImportExcel,
|
||||
onExportExcel,
|
||||
exportLoading,
|
||||
onExportExcelOverride,
|
||||
cgBIBtnMap,
|
||||
getQueryButtonCfg,
|
||||
getResetButtonCfg,
|
||||
getFormConfirmButtonCfg,
|
||||
cgTopButtonList,
|
||||
cgLinkButtonList,
|
||||
cgButtonJsHandler,
|
||||
cgButtonActionHandler,
|
||||
cgButtonLinkHandler,
|
||||
handleBatchDelete,
|
||||
handleAddTestData,
|
||||
testDataLoading,
|
||||
testDataBtnShow,
|
||||
currentTableName,
|
||||
loadData,
|
||||
tableType,
|
||||
isConfigCurRoute,
|
||||
|
||||
// table-slot
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
|
||||
// 操作列
|
||||
getActions,
|
||||
getDropDownActions,
|
||||
|
||||
// 弹窗
|
||||
registerModal,
|
||||
registerCustomModal,
|
||||
registerImportModal,
|
||||
registerDetailModal,
|
||||
importUrl,
|
||||
handleFormConfig,
|
||||
onlinePopModalRef,
|
||||
|
||||
//其他
|
||||
tableReloading,
|
||||
handleSubmitFlow,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
hrefMainTableId,
|
||||
onlineExtConfigJson,
|
||||
registerOnlineHrefModal,
|
||||
registerPopModal,
|
||||
popTableId,
|
||||
registerBpmModal,
|
||||
handleClickFieldHref,
|
||||
pageLoading,
|
||||
};
|
||||
return that;
|
||||
},
|
||||
|
||||
// 1引入了loadsh console.log(that.simpleDateFormat(new Date().getTime(),'yyyy-MM-dd'));
|
||||
// 2. value的问题
|
||||
// 3. 变量位置改变后需要 重写api
|
||||
// 1添加按钮的时候 预留出样式对象 然后js增强中设置样式对象
|
||||
// 2直接设置css字符串 然后通过js document 往head里面增加css片段 全局生效
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
/** [表格主题样式一] 表格强制列不换行 */
|
||||
.j-table-force-nowrap {
|
||||
td,
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ant-table-selection-column {
|
||||
padding: 12px 22px !important;
|
||||
}
|
||||
|
||||
/** 列自适应,弊端会导致列宽失效 */
|
||||
&.ant-table-wrapper .ant-table-content {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
.online-cell-image {
|
||||
height: 25px !important;
|
||||
margin: 0 auto;
|
||||
max-width: 80px;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<BasicModal :title="title" @cancel="onCloseEvent" :enableComment="enableComment" :width="modalWidth" v-bind="$attrs" :maxHeight="600" @register="registerModal" wrapClassName="jeecg-online-modal" @ok="handleSubmit" @commentOpen="handleCommentOpen">
|
||||
<template #footer>
|
||||
<a-row>
|
||||
<a-col :span='24 - commentSpan'>
|
||||
<a-button
|
||||
v-for="btn in cgButtonList"
|
||||
:key="btn.id"
|
||||
type="primary"
|
||||
@click="handleCgButtonClick(btn.optType, btn.buttonCode)"
|
||||
:preIcon="btn.buttonIcon ? 'ant-design:' + btn.buttonIcon : ''"
|
||||
>
|
||||
{{ btn.buttonName }}
|
||||
</a-button>
|
||||
|
||||
<a-button
|
||||
v-if="!disableSubmit && confirmBtnCfg.enabled"
|
||||
key="submit"
|
||||
type="primary"
|
||||
:preIcon="confirmBtnCfg.buttonIcon"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
<span>{{ confirmBtnCfg.buttonName }}</span>
|
||||
</a-button>
|
||||
<a-button v-if="cancelBtnCfg.enabled" key="back" @click="handleCancel">
|
||||
<span>{{ cancelBtnCfg.buttonName }}</span>
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<online-form
|
||||
v-bind="$attrs"
|
||||
ref="onlineFormCompRef"
|
||||
:id="id"
|
||||
:disabled="disableSubmit"
|
||||
:form-template="formTemplate"
|
||||
:isTree="isTreeForm"
|
||||
:pidField="pidFieldName"
|
||||
:themeTemplate="themeTemplate"
|
||||
:cgBIBtnMap="cgBIBtnMap"
|
||||
:buttonSwitch="buttonSwitch"
|
||||
@rendered="renderSuccess"
|
||||
@success="handleSuccess"
|
||||
>
|
||||
</online-form>
|
||||
|
||||
<template #comment>
|
||||
<comment-panel ref="commentPanelRef" :tableId="tableId" :tableName="tableName" :dataId="formDataId"></comment-panel>
|
||||
</template>
|
||||
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch, ref } from 'vue';
|
||||
import { BasicModal } from '/@/components/Modal';
|
||||
import OnlineForm from '../comp/OnlineForm.vue';
|
||||
import { useAutoModal } from '../../hooks/auto/useAutoModal';
|
||||
import CommentPanel from '/@/components/jeecg/comment/CommentPanel.vue'
|
||||
import { ERPSUBTABLE } from '../../util/constant';
|
||||
export default defineComponent({
|
||||
name: 'OnlineAutoModal',
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
// 为了区分来源,编码时主要是erp子表有特殊处理
|
||||
source: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
buttonSwitch: Object,
|
||||
cgBIBtnMap: Object,
|
||||
confirmBtnCfg: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
enabled: true,
|
||||
buttonName: '确定',
|
||||
buttonIcon: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
cancelBtnCfg: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
enabled: true,
|
||||
buttonName: '关闭',
|
||||
buttonIcon: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
BasicModal,
|
||||
OnlineForm,
|
||||
CommentPanel
|
||||
},
|
||||
emits: ['success', 'register', 'formConfig'],
|
||||
setup(props, { emit }) {
|
||||
console.log('进入表单弹框》》》》modal');
|
||||
const commentPanelRef = ref();
|
||||
const commentSpan = ref(0);
|
||||
function reloadComment(){
|
||||
if(commentPanelRef.value)
|
||||
commentPanelRef.value.reload();
|
||||
}
|
||||
const {
|
||||
title,
|
||||
modalWidth,
|
||||
registerModal,
|
||||
closeModal,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
disableSubmit,
|
||||
handleSubmit,
|
||||
submitLoading,
|
||||
handleCancel,
|
||||
handleFormConfig,
|
||||
onlineFormCompRef,
|
||||
formTemplate,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
renderSuccess,
|
||||
formRendered,
|
||||
tableName,
|
||||
tableId,
|
||||
formDataId,
|
||||
enableComment,
|
||||
onCloseEvent,
|
||||
themeTemplate,
|
||||
} = useAutoModal(false, { emit }, reloadComment);
|
||||
|
||||
function handleSuccess(formData) {
|
||||
emit('success', formData);
|
||||
closeModal();
|
||||
// 提交完成 触发关闭事件
|
||||
onCloseEvent();
|
||||
}
|
||||
|
||||
// 监听id变化 表单重新渲染
|
||||
watch(() => props.id, renderFormItems, { immediate: true });
|
||||
async function renderFormItems() {
|
||||
formRendered.value = false;
|
||||
if (!props.id) {
|
||||
return;
|
||||
}
|
||||
console.log('重新渲染表单》》》》modal');
|
||||
// update-begin--author:liaozhiyang---date:20240426---for:【issues/6124】当用户没有【Online表单开发】页面的权限时用户无权查看从表新增和详情的数据
|
||||
const params: any = {};
|
||||
if (props.source === ERPSUBTABLE) {
|
||||
params.tabletype = 3;
|
||||
}
|
||||
await handleFormConfig(props.id, params);
|
||||
// update-end--author:liaozhiyang---date:20240426---for:【issues/6124】当用户没有【Online表单开发】页面的权限时用户无权查看从表新增和详情的数据
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240528---for:【TV360X-485】开启评论之后弹窗按钮居右隔一个评论的距离
|
||||
const handleCommentOpen = (visible, span) => {
|
||||
console.log('评论是否展开:', visible);
|
||||
commentSpan.value = span;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240528---for:【TV360X-485】开启评论之后弹窗按钮居右隔一个评论的距离
|
||||
const that = {
|
||||
title,
|
||||
onlineFormCompRef,
|
||||
renderSuccess,
|
||||
registerModal,
|
||||
handleSubmit,
|
||||
handleSuccess,
|
||||
handleCancel,
|
||||
modalWidth,
|
||||
formTemplate,
|
||||
disableSubmit,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
submitLoading,
|
||||
tableName,
|
||||
tableId,
|
||||
formDataId,
|
||||
enableComment,
|
||||
commentPanelRef,
|
||||
onCloseEvent,
|
||||
themeTemplate,
|
||||
handleCommentOpen,
|
||||
commentSpan,
|
||||
};
|
||||
|
||||
return that;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="modalProps"
|
||||
:style="modalStyle"
|
||||
@register="registerModal"
|
||||
wrapClassName="jeecg-online-modal2"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<template #footer>
|
||||
<a-button key="submit" type="primary" @click="handleSubmit"> {{modalProps.okText ?? '确定'}}</a-button>
|
||||
<a-button key="back" @click="handleCancel">{{ modalProps.cancelText ?? '关闭' }}</a-button>
|
||||
</template>
|
||||
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<!-- online表单 -->
|
||||
<online-form
|
||||
v-if="isOnlineForm"
|
||||
ref="onlineFormCompRef"
|
||||
:id="id"
|
||||
:form-template="formTemplate"
|
||||
@rendered="renderSuccess"
|
||||
@success="handleSuccess"
|
||||
modalClass="jeecg-online-modal2"
|
||||
>
|
||||
</online-form>
|
||||
|
||||
<!-- 自定义表单 -->
|
||||
<component
|
||||
v-else
|
||||
ref="customFormRef"
|
||||
:url="customFormComponent.url"
|
||||
:is="customFormComponent.is"
|
||||
:row="customFormComponent.row"
|
||||
@close="handleSuccess"
|
||||
>
|
||||
</component>
|
||||
</a-spin>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, ref, watch, nextTick, defineAsyncComponent, markRaw } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import OnlineForm from '../comp/OnlineForm.vue';
|
||||
import { importViewsFile } from '/@/utils';
|
||||
import { omit } from 'lodash-es';
|
||||
|
||||
/**
|
||||
* open函数的参数
|
||||
*/
|
||||
interface ModalConfig {
|
||||
row: any;
|
||||
hide?: string[];
|
||||
show?: string[];
|
||||
requestUrl?: string;
|
||||
tableType?: string;
|
||||
foreignKeys?: string;
|
||||
formComponent?: string;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
const DEF_CONFIG: ModalConfig = {
|
||||
code: '',
|
||||
row: {},
|
||||
hide: [],
|
||||
show: [],
|
||||
requestUrl: '',
|
||||
tableType: '',
|
||||
foreignKeys: '',
|
||||
formComponent: '',
|
||||
};
|
||||
/**
|
||||
* 自定义弹窗
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'OnlineCustomModal',
|
||||
components: {
|
||||
OnlineForm,
|
||||
BasicModal,
|
||||
},
|
||||
setup(_props, { emit, attrs }) {
|
||||
const onlineFormCompRef = ref();
|
||||
const modalProps = ref({});
|
||||
// online表单的配置id
|
||||
const id = ref('');
|
||||
|
||||
let onlineHideFields = [];
|
||||
let onlineShowFields = [];
|
||||
let onlineFormEditUrl = '';
|
||||
// 当前编辑的数据
|
||||
let currentRowData = {};
|
||||
const url = {
|
||||
loadFormItems: '/online/cgform/api/getFormItem/',
|
||||
optPre: '/online/cgform/api/form/',
|
||||
};
|
||||
const modalStyle = { position: 'relative' };
|
||||
|
||||
const confirmLoading = ref(false);
|
||||
// 表单是否渲染完成
|
||||
const formRendered = ref(false);
|
||||
// 渲染完成改变状态
|
||||
function renderSuccess() {
|
||||
formRendered.value = true;
|
||||
}
|
||||
|
||||
//是否是online表单
|
||||
const isOnlineForm = ref(true);
|
||||
|
||||
// 弹框显示 触发onlineFormCompRef---show
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (params) => {
|
||||
setModalProps({ confirmLoading: false });
|
||||
resetParams(params);
|
||||
await nextTick(async () => {
|
||||
if (!params.formComponent) {
|
||||
//没有申明组件 走online表单
|
||||
await showOnlineForm();
|
||||
} else {
|
||||
showCustomForm(params.formComponent);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开弹窗
|
||||
* @param params
|
||||
*/
|
||||
function resetParams(params) {
|
||||
// code row formComponent hide show
|
||||
let options = Object.assign({}, DEF_CONFIG, params);
|
||||
id.value = options.code;
|
||||
// update-begin--author:liaozhiyang---date:20250818---for:【issues/8672】js增强弹窗支持basicModal的props
|
||||
modalProps.value = {
|
||||
title: '自定义弹框',
|
||||
width: 600,
|
||||
...attrs,
|
||||
...omit(params, ['row', 'formComponent', 'hide', 'show', 'requestUrl']),
|
||||
};
|
||||
// update-end--author:liaozhiyang---date:20250818---for:【issues/8672】js增强弹窗支持basicModal的props
|
||||
onlineHideFields = options.hide || [];
|
||||
onlineShowFields = options.show || [];
|
||||
onlineFormEditUrl = getOnlineFormEditUrl(options.requestUrl);
|
||||
currentRowData = options.row;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表单数据编辑地址-对两个表单都有效
|
||||
*/
|
||||
function getOnlineFormEditUrl(requestUrl) {
|
||||
if (requestUrl) {
|
||||
return requestUrl;
|
||||
} else {
|
||||
return url.optPre + id.value;
|
||||
}
|
||||
}
|
||||
|
||||
const submitLoading = ref(false);
|
||||
function handleSubmit() {
|
||||
submitLoading.value = true;
|
||||
if (isOnlineForm.value === true) {
|
||||
onlineFormCompRef.value.handleSubmit();
|
||||
} else {
|
||||
customFormRef.value.handleSubmit();
|
||||
}
|
||||
setTimeout(() => {
|
||||
submitLoading.value = true;
|
||||
}, 3500);
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function handleSuccess(formData) {
|
||||
emit('success', formData);
|
||||
closeModal();
|
||||
}
|
||||
|
||||
/*-------------------------------------online表单-----------------------------------------------*/
|
||||
|
||||
/**
|
||||
* 显示online表单
|
||||
*/
|
||||
async function showOnlineForm() {
|
||||
isOnlineForm.value = true;
|
||||
await getRefPromise(formRendered);
|
||||
//处理字段显示隐藏
|
||||
onlineFormCompRef.value.handleCustomFormSh(onlineShowFields, onlineHideFields);
|
||||
//回显表单数据
|
||||
onlineFormCompRef.value.handleCustomFormEdit(currentRowData, onlineFormEditUrl);
|
||||
}
|
||||
|
||||
// 模板风格-默认1列
|
||||
const formTemplate = ref(1);
|
||||
// 监听id变化 表单重新渲染
|
||||
watch(id, renderFormItems, { immediate: true });
|
||||
async function renderFormItems() {
|
||||
formRendered.value = false;
|
||||
if (!id.value) {
|
||||
return;
|
||||
}
|
||||
console.log('重新渲染表单》》》》modal');
|
||||
let result: any = await loadFormItems();
|
||||
// modal页面只处理按钮、JS增强、弹框宽度
|
||||
let dataFormTemplate = result.head.formTemplate;
|
||||
formTemplate.value = dataFormTemplate ? Number(dataFormTemplate) : 1;
|
||||
nextTick(async () => {
|
||||
let myForm = (await getRefPromise(onlineFormCompRef)) as any;
|
||||
myForm.createRootProperties(result);
|
||||
});
|
||||
}
|
||||
|
||||
function loadFormItems() {
|
||||
let url = `/online/cgform/api/getFormItem/${id.value}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
defHttp
|
||||
.get({ url }, { isTransformResponse: false })
|
||||
.then((res) => {
|
||||
console.log('表单结果》》modal:', res);
|
||||
if (res.success) {
|
||||
resolve(res.result);
|
||||
} else {
|
||||
reject(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
/*-------------------------------------online表单-----------------------------------------------*/
|
||||
|
||||
/*-------------------------------------自定义表单-----------------------------------------------*/
|
||||
const customFormRef = ref();
|
||||
|
||||
const customFormComponent = reactive({
|
||||
url: '',
|
||||
is: '',
|
||||
row: {},
|
||||
});
|
||||
|
||||
/**
|
||||
* 显示自定义表单
|
||||
*/
|
||||
function showCustomForm(formComponent) {
|
||||
isOnlineForm.value = false;
|
||||
customFormComponent.url = onlineFormEditUrl;
|
||||
customFormComponent.row = currentRowData;
|
||||
customFormComponent.is = markRaw(defineAsyncComponent(() => importViewsFile(formComponent)));
|
||||
}
|
||||
/*-------------------------------------自定义表单-----------------------------------------------*/
|
||||
|
||||
return {
|
||||
//modal
|
||||
registerModal,
|
||||
modalProps,
|
||||
modalStyle,
|
||||
handleSubmit,
|
||||
handleCancel,
|
||||
|
||||
// online表单
|
||||
id,
|
||||
onlineFormCompRef,
|
||||
formTemplate,
|
||||
renderSuccess,
|
||||
|
||||
//自定义表单
|
||||
customFormRef,
|
||||
customFormComponent,
|
||||
|
||||
//通用
|
||||
open,
|
||||
isOnlineForm,
|
||||
confirmLoading,
|
||||
submitLoading,
|
||||
handleSuccess,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<BasicModal :title="title" :width="modalWidth" :maxHeight="600" :enableComment="enableComment" :defaultFullscreen="false" v-bind="$attrs" @register="registerModal" wrapClassName="jeecg-online-detail-modal">
|
||||
<template #footer>
|
||||
<a-button v-if="cancelBtnCfg.enabled" key="back" @click="handleCancel">
|
||||
<span>{{ cancelBtnCfg.buttonName }}</span>
|
||||
</a-button>
|
||||
<slot name="footerBtn"></slot>
|
||||
</template>
|
||||
<online-form-detail ref="onlineFormCompRef" :id="id" :form-template="formTemplate" :show-sub="showSub" :themeTemplate="themeTemplate" @rendered="renderSuccess" />
|
||||
|
||||
<template #comment>
|
||||
<comment-panel ref="commentPanelRef" :tableName="tableName" :dataId="formDataId"></comment-panel>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch, ref } from 'vue';
|
||||
import { BasicModal } from '/@/components/Modal';
|
||||
import OnlineFormDetail from '../comp/OnlineFormDetail.vue';
|
||||
import { useAutoModal } from '../../hooks/auto/useAutoModal';
|
||||
import CommentPanel from '/@/components/jeecg/comment/CommentPanel.vue'
|
||||
import { ERPSUBTABLE, INNER_TABLE } from '../../util/constant';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OnlineDetailModal',
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
// 为了区分来源,编码时主要是erp子表有特殊处理
|
||||
source: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
cancelBtnCfg: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
enabled: true,
|
||||
buttonName: '关闭',
|
||||
buttonIcon: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
BasicModal,
|
||||
OnlineFormDetail,
|
||||
CommentPanel
|
||||
},
|
||||
emits: ['success', 'register','formConfig'],
|
||||
setup(props, {emit}) {
|
||||
console.log('进入表单弹框》》》》modal');
|
||||
|
||||
const commentPanelRef = ref();
|
||||
function reloadComment(){
|
||||
if(commentPanelRef.value)
|
||||
commentPanelRef.value.reload();
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
modalWidth,
|
||||
registerModal,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
disableSubmit,
|
||||
handleSubmit,
|
||||
submitLoading,
|
||||
handleCancel,
|
||||
handleFormConfig,
|
||||
onlineFormCompRef,
|
||||
formTemplate,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
renderSuccess,
|
||||
formRendered,
|
||||
showSub,
|
||||
tableName,
|
||||
formDataId,
|
||||
enableComment,
|
||||
themeTemplate,
|
||||
} = useAutoModal(false, { emit }, reloadComment);
|
||||
|
||||
// 监听id变化 表单重新渲染
|
||||
watch(() => props.id, renderFormItems, { immediate: true });
|
||||
async function renderFormItems() {
|
||||
formRendered.value = false;
|
||||
if (!props.id) {
|
||||
return;
|
||||
}
|
||||
console.log('重新渲染表单》》》》modal');
|
||||
// update-begin--author:liaozhiyang---date:20240426---for:【issues/6124】当用户没有【Online表单开发】页面的权限时用户无权查看从表新增和详情的数据
|
||||
let params: any = {};
|
||||
//update-begin---author:wangshuai---date:2025-10-21---for:【issues/8933】内嵌子表主题(一对多)列表点+号展开明细提示:无权限访问(操作)---
|
||||
if (props.source === ERPSUBTABLE || props.source === INNER_TABLE) {
|
||||
//update-end---author:wangshuai---date:2025-10-21---for:【issues/8933】内嵌子表主题(一对多)列表点+号展开明细提示:无权限访问(操作)---
|
||||
params.tabletype = 3;
|
||||
}
|
||||
await handleFormConfig(props.id, params);
|
||||
// update-end--author:liaozhiyang---date:20240426---for:【issues/6124】当用户没有【Online表单开发】页面的权限时用户无权查看从表新增和详情的数据
|
||||
}
|
||||
|
||||
const that = {
|
||||
title,
|
||||
onlineFormCompRef,
|
||||
renderSuccess,
|
||||
registerModal,
|
||||
handleSubmit,
|
||||
handleCancel,
|
||||
modalWidth,
|
||||
formTemplate,
|
||||
disableSubmit,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
submitLoading,
|
||||
showSub,
|
||||
tableName,
|
||||
formDataId,
|
||||
enableComment,
|
||||
commentPanelRef,
|
||||
themeTemplate
|
||||
};
|
||||
|
||||
return that;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div :class="['p-2']" class="online-wrap">
|
||||
<Card :bordered="false">
|
||||
<spin :spinning="loading">
|
||||
<div :class="['wrap', `online-add-${ID}`]" ref="wrapRef">
|
||||
<OnlineForm v-if="false"></OnlineForm>
|
||||
<!-- 新增(编辑)弹框 -->
|
||||
<OnlineAutoModal
|
||||
v-if="token"
|
||||
:id="ID"
|
||||
:maskClosable="false"
|
||||
@register="registerModal"
|
||||
:getContainer="getContainer"
|
||||
@formConfig="handleFormConfig"
|
||||
@success="success"
|
||||
:height="contentHeight"
|
||||
/>
|
||||
</div>
|
||||
</spin>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, unref } from 'vue';
|
||||
import { Card, Spin } from 'ant-design-vue';
|
||||
// OnlineForm 这个必须引用,setup模式必须在template里面使用。否则url进入会报错
|
||||
import OnlineForm from '../comp/OnlineForm.vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import OnlineAutoModal from './OnlineAutoModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useFormUrl } from '../../hooks/auto/useFormUrl';
|
||||
const { ID, onlineTableContext, onlineExtConfigJson, handleFormConfig } = useOnlineTableContext();
|
||||
const { createMessage: $message } = useMessage();
|
||||
const wrapRef = ref(null);
|
||||
const contentHeight = ref(400);
|
||||
const loading = ref(true);
|
||||
const router = useRouter();
|
||||
|
||||
const { token } = useFormUrl();
|
||||
if (!unref(token)) {
|
||||
throw new Error('token不存在~');
|
||||
}
|
||||
if (!ID.value) {
|
||||
$message.warning('地址错误, 配置ID不存在!');
|
||||
throw new Error('地址错误, 配置ID不存在!');
|
||||
}
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
watch(
|
||||
() => wrapRef.value,
|
||||
(elem: HTMLElement) => {
|
||||
// 获取页面实际高度
|
||||
contentHeight.value = elem.offsetHeight - 60;
|
||||
}
|
||||
);
|
||||
// 处理列表button
|
||||
const { registerModal, handleAdd } = useListButton(onlineTableContext, onlineExtConfigJson);
|
||||
const getContainer = (node) => {
|
||||
return document.querySelector(`.online-add-${ID.value}`);
|
||||
};
|
||||
const success = () => {
|
||||
setTimeout(() => {
|
||||
handleAdd(false);
|
||||
setTimeout(() => {
|
||||
router.push({ path: '/online/formUrlSuccess' });
|
||||
}, 1e3);
|
||||
}, 0);
|
||||
};
|
||||
setTimeout(() => {
|
||||
handleAdd(false);
|
||||
loading.value = false;
|
||||
}, 1e3);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.online-wrap {
|
||||
height: 100%;
|
||||
// position: relative;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
padding: 0;
|
||||
:deep(.ant-card) {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
border: none;
|
||||
.ant-card-body {
|
||||
height: 100%;
|
||||
padding: 40px 16px;
|
||||
}
|
||||
.ant-modal-footer {
|
||||
& > :last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.ant-spin-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.wrap {
|
||||
height: 100%;
|
||||
> div {
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.ant-modal-root) {
|
||||
height: 100%;
|
||||
.ant-modal-mask {
|
||||
height: 0;
|
||||
}
|
||||
.ant-modal-wrap {
|
||||
position: static;
|
||||
height: 100%;
|
||||
}
|
||||
.ant-modal {
|
||||
position: static;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: scale(1) !important;
|
||||
opacity: 1;
|
||||
animation-name: none !important;
|
||||
}
|
||||
.ant-modal-content {
|
||||
position: static;
|
||||
box-shadow: none;
|
||||
.scroll-container {
|
||||
padding: 0;
|
||||
.scrollbar__view > div {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-modal-header {
|
||||
display: none;
|
||||
}
|
||||
.ant-modal-close {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<div :class="['p-2']" class="online-wrap">
|
||||
<Card :bordered="false">
|
||||
<spin :spinning="loading">
|
||||
<div :class="['wrap', `online-detail-${ID}`]" ref="wrapRef">
|
||||
<OnlineForm v-if="false"></OnlineForm>
|
||||
<!-- 详情弹框 -->
|
||||
<OnlineDetailModal
|
||||
v-if="token"
|
||||
:id="ID"
|
||||
:maskClosable="false"
|
||||
@register="registerDetailModal"
|
||||
@success="success"
|
||||
:getContainer="getContainer"
|
||||
:height="contentHeight"
|
||||
/>
|
||||
</div>
|
||||
</spin>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, unref } from 'vue';
|
||||
import { Card, Spin } from 'ant-design-vue';
|
||||
// OnlineForm 这个必须引用,setup模式必须在template里面使用。否则url进入会报错
|
||||
import OnlineForm from '../comp/OnlineForm.vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useRoute } from 'vue-router';
|
||||
import OnlineDetailModal from './OnlineDetailModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { useFormUrl } from '../../hooks/auto/useFormUrl';
|
||||
const route = useRoute();
|
||||
const { ID, onlineTableContext, onlineExtConfigJson } = useOnlineTableContext();
|
||||
const { createMessage: $message } = useMessage();
|
||||
const wrapRef = ref(null);
|
||||
const contentHeight = ref(400);
|
||||
const loading = ref(true);
|
||||
|
||||
const { token } = useFormUrl();
|
||||
if (!unref(token)) {
|
||||
throw new Error('token不存在~');
|
||||
}
|
||||
if (!ID.value || !route.params.dataId) {
|
||||
$message.warning('地址错误, 配置ID不存在!');
|
||||
throw new Error('地址错误, 配置ID不存在!');
|
||||
}
|
||||
watch(
|
||||
() => wrapRef.value,
|
||||
(elem: HTMLElement) => {
|
||||
// 获取页面实际高度
|
||||
contentHeight.value = elem.offsetHeight;
|
||||
}
|
||||
);
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
// 处理列表button
|
||||
const { registerDetailModal, openDetailModal } = useListButton(onlineTableContext, onlineExtConfigJson);
|
||||
const getContainer = (node) => {
|
||||
return document.querySelector(`.online-detail-${ID.value}`);
|
||||
};
|
||||
setTimeout(() => {
|
||||
openDetailModal(true, {
|
||||
isUpdate: true,
|
||||
disableSubmit: true,
|
||||
record: {
|
||||
id: route.params.dataId,
|
||||
},
|
||||
});
|
||||
loading.value = false;
|
||||
}, 1e3);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.online-wrap {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
:deep(.ant-card) {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
border: none;
|
||||
.ant-card-body {
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.ant-spin-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.wrap {
|
||||
height: 100%;
|
||||
> div {
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.ant-modal-root) {
|
||||
height: 100%;
|
||||
.ant-modal-mask {
|
||||
height: 0;
|
||||
}
|
||||
.ant-modal-wrap {
|
||||
position: static;
|
||||
height: 100%;
|
||||
}
|
||||
.ant-modal {
|
||||
position: static;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: scale(1) !important;
|
||||
opacity: 1;
|
||||
animation-name: none !important;
|
||||
}
|
||||
.ant-modal-content {
|
||||
position: static;
|
||||
box-shadow: none;
|
||||
.scroll-container {
|
||||
padding: 0;
|
||||
.scrollbar__view > div {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-modal-header,
|
||||
.ant-modal-footer {
|
||||
display: none;
|
||||
}
|
||||
.ant-modal-close {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<div :class="['p-2']" class="online-wrap">
|
||||
<Card :bordered="false">
|
||||
<spin :spinning="loading">
|
||||
<div :class="['wrap', `online-edit-${ID}`]" ref="wrapRef">
|
||||
<OnlineForm v-if="false"></OnlineForm>
|
||||
<!-- 新增(编辑)弹框 -->
|
||||
<OnlineAutoModal
|
||||
v-if="token"
|
||||
:id="ID"
|
||||
:maskClosable="false"
|
||||
@register="registerModal"
|
||||
:getContainer="getContainer"
|
||||
@formConfig="handleFormConfig"
|
||||
@success="success"
|
||||
:height="contentHeight"
|
||||
/>
|
||||
</div>
|
||||
</spin>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { Card, Spin } from 'ant-design-vue';
|
||||
// OnlineForm 这个必须引用,setup模式必须在template里面使用。否则url进入会报错
|
||||
import OnlineForm from '../comp/OnlineForm.vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import OnlineAutoModal from './OnlineAutoModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { useFormUrl } from '../../hooks/auto/useFormUrl';
|
||||
const { ID, onlineTableContext, onlineExtConfigJson, handleFormConfig } = useOnlineTableContext();
|
||||
const { createMessage: $message } = useMessage();
|
||||
const route = useRoute();
|
||||
const wrapRef = ref(null);
|
||||
const contentHeight = ref(400);
|
||||
const loading = ref(true);
|
||||
const router = useRouter();
|
||||
|
||||
const { token } = useFormUrl();
|
||||
if (!unref(token)) {
|
||||
throw new Error('token不存在~');
|
||||
}
|
||||
if (!ID.value || !route.params.dataId) {
|
||||
$message.warning('地址错误, 配置ID不存在!');
|
||||
throw new Error('地址错误, 配置ID不存在!');
|
||||
}
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
watch(
|
||||
() => wrapRef.value,
|
||||
(elem: HTMLElement) => {
|
||||
// 获取页面实际高度
|
||||
contentHeight.value = elem.offsetHeight - 60;
|
||||
}
|
||||
);
|
||||
// 处理列表button
|
||||
const { registerModal, handleEdit } = useListButton(onlineTableContext, onlineExtConfigJson);
|
||||
const getContainer = (node) => {
|
||||
return document.querySelector(`.online-edit-${ID.value}`);
|
||||
};
|
||||
const success = () => {
|
||||
setTimeout(() => {
|
||||
handleEdit({ id: route.params.dataId });
|
||||
router.push({ path: '/online/formUrlSuccess' });
|
||||
}, 0);
|
||||
};
|
||||
setTimeout(() => {
|
||||
token && handleEdit({ id: route.params.dataId });
|
||||
loading.value = false;
|
||||
}, 1e3);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.online-wrap {
|
||||
height: 100%;
|
||||
// position: relative;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
padding: 0;
|
||||
:deep(.ant-card) {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
border: none;
|
||||
.ant-card-body {
|
||||
height: 100%;
|
||||
padding: 40px 16px;
|
||||
}
|
||||
.ant-modal-footer {
|
||||
& > :last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.ant-spin-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.wrap {
|
||||
height: 100%;
|
||||
> div {
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.ant-modal-root) {
|
||||
height: 100%;
|
||||
.ant-modal-mask {
|
||||
height: 0;
|
||||
}
|
||||
.ant-modal-wrap {
|
||||
position: static;
|
||||
height: 100%;
|
||||
}
|
||||
.ant-modal {
|
||||
position: static;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: scale(1) !important;
|
||||
opacity: 1;
|
||||
animation-name: none !important;
|
||||
}
|
||||
.ant-modal-content {
|
||||
position: static;
|
||||
box-shadow: none;
|
||||
.scroll-container {
|
||||
padding: 0;
|
||||
.scrollbar__view > div {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-modal-header {
|
||||
display: none;
|
||||
}
|
||||
.ant-modal-close {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div :class="['p-2']" class="online-wrap">
|
||||
<div class="contentArea">
|
||||
<svg t="1709697840296" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1485">
|
||||
<path
|
||||
d="M874.119618 149.859922A510.816461 510.816461 0 0 0 511.997 0.00208a509.910462 509.910462 0 0 0-362.119618 149.857842c-199.817789 199.679789-199.817789 524.581447 0 724.260236a509.969462 509.969462 0 0 0 362.119618 149.857842A508.872463 508.872463 0 0 0 874.119618 874.120158c199.836789-199.679789 199.836789-524.581447 0-724.260236zM814.94268 378.210681L470.999043 744.132295a15.359984 15.359984 0 0 1-5.887994 4.095996c-1.751998 1.180999-2.913997 2.362998-5.276994 2.913997a34.499964 34.499964 0 0 1-13.469986 2.914997 45.547952 45.547952 0 0 1-12.897986-2.303998l-4.095996-2.363997a45.291952 45.291952 0 0 1-7.009992-4.095996l-196.902793-193.789796a34.126964 34.126964 0 0 1-10.555989-25.186973c0-9.37399 3.583996-18.74698 9.98399-25.186974a36.429962 36.429962 0 0 1 50.372947 0l169.98382 167.423824L763.389735 330.220732a37.059961 37.059961 0 0 1 50.371947-1.732998 33.647965 33.647965 0 0 1 11.165988 25.186973 35.544963 35.544963 0 0 1-9.98399 24.575974v-0.04z m0 0"
|
||||
fill="currentColor"
|
||||
p-id="1486"
|
||||
></path>
|
||||
</svg>
|
||||
<h1>提交成功</h1>
|
||||
</div>
|
||||
<div class="btnArea">
|
||||
<a-button @click="getBack()">返回</a-button>
|
||||
<a-button @click="getBack()">关闭</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
const getBack = () => {
|
||||
router.go(-1);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.online-wrap {
|
||||
height: 100%;
|
||||
// position: relative;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.contentArea {
|
||||
text-align: center;
|
||||
padding-top: 10%;
|
||||
margin-bottom: 24px;
|
||||
svg {
|
||||
width: 80px;
|
||||
color: #40c409;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
.btnArea {
|
||||
width: 150px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,537 @@
|
||||
<template>
|
||||
<div ref="cgformErpListRef" class="p-2 cgformErpList">
|
||||
<div class="content">
|
||||
|
||||
<!-- 加载中骨架屏 -->
|
||||
<a-skeleton v-if="tableReloading" active />
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<online-query-form
|
||||
v-show="!tableReloading"
|
||||
ref="onlineQueryFormOuter"
|
||||
:id="ID"
|
||||
:queryBtnCfg="getQueryButtonCfg"
|
||||
:resetBtnCfg="getResetButtonCfg"
|
||||
@search="queryWithCondition"
|
||||
@loaded="onQueryFormLoaded"
|
||||
/>
|
||||
|
||||
<!-- 列表 -->
|
||||
<BasicTable
|
||||
v-if="!tableReloading"
|
||||
ref="onlineTable"
|
||||
rowKey="jeecg_row_key"
|
||||
:canResize="true"
|
||||
:bordered="true"
|
||||
:showIndexColumn="false"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="pagination"
|
||||
:rowSelection="rowSelection"
|
||||
:actionColumn="actionColumn"
|
||||
:showTableSetting="true"
|
||||
:clickToRowSelect="true"
|
||||
:scroll="scroll"
|
||||
@table-redo="reload"
|
||||
:tableSetting="tableSetting"
|
||||
:class="{ 'j-table-force-nowrap': enableScrollBar }"
|
||||
@change="handleChangeInTable"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
v-if="buttonSwitch.add && cgBIBtnMap['add'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['add'].buttonIcon"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<span>{{ cgBIBtnMap['add'].buttonName }}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="buttonSwitch.import && cgBIBtnMap['import'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['import'].buttonIcon"
|
||||
@click="onImportExcel"
|
||||
>
|
||||
<span>{{ cgBIBtnMap['import'].buttonName }}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="buttonSwitch.export && cgBIBtnMap['export'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['export'].buttonIcon"
|
||||
:loading="exportLoading"
|
||||
@click="onExportExcelOverride"
|
||||
>
|
||||
<span>{{ cgBIBtnMap['export'].buttonName }}</span>
|
||||
</a-button>
|
||||
|
||||
<!-- 自定义按钮 -->
|
||||
<template v-if="cgTopButtonList && cgTopButtonList.length > 0" v-for="(item, index) in cgTopButtonList">
|
||||
<a-button
|
||||
v-if="item.optType == 'js'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonJsHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-else-if="item.optType == 'action'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonActionHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<a-button
|
||||
v-show="selectedKeys.length > 0"
|
||||
v-if="buttonSwitch.batch_delete && cgBIBtnMap['batch_delete'].enabled"
|
||||
:preIcon="cgBIBtnMap['batch_delete'].buttonIcon"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
<span>{{cgBIBtnMap['batch_delete'].buttonName}}</span>
|
||||
</a-button>
|
||||
|
||||
<online-super-query
|
||||
v-if="buttonSwitch.super_query && cgBIBtnMap['super_query'].enabled"
|
||||
ref="superQueryButtonRef"
|
||||
online
|
||||
:status="superQueryStatus"
|
||||
:queryBtnCfg="cgBIBtnMap['super_query']"
|
||||
@search="handleSuperSearch"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #fileSlot="{ text, record, column }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
|
||||
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download" size="small" @click="downloadRowFile(text, record, column, ID)">
|
||||
下载
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #imgSlot="{ text }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
|
||||
<img v-else :src="getImgView(text)" alt="图片不存在" class="online-cell-image" @click="viewOnlineCellImage(text)" />
|
||||
</template>
|
||||
|
||||
<template #htmlSlot="{ text, column, record }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
<template v-if="column.fieldHref">
|
||||
<a v-html="text" @click="handleClickFieldHref(column.fieldHref, record)"></a>
|
||||
</template>
|
||||
<div v-else v-html="text"></div>
|
||||
<!-- update-end--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
</template>
|
||||
|
||||
<template #pcaSlot="{ text, column }">
|
||||
<div :title="getPcaText(text, column)">{{ getPcaText(text, column) }}</div>
|
||||
</template>
|
||||
|
||||
<template #dateSlot="{ text, column }">
|
||||
<span>{{ getFormatDate(text, column) }}</span>
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!--子表表格tab-->
|
||||
<a-tabs v-if="subTableData?.length" animated v-model:activeKey="tabActiveKey" style="margin: 10px">
|
||||
<a-tab-pane v-for="(item, index) in subTableData" :tab="item.description" :key="index" forceRender>
|
||||
<OnlCgformErpSubTable :data="item" :mainTableSelectedRowRcord="selectedRowRcord" @getSource="getSource" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
||||
<!-- 表单新增、修改弹框 -->
|
||||
<OnlineAutoModal
|
||||
@register="registerModal"
|
||||
:id="ID"
|
||||
:subTableSource="erpAllSubTableSource"
|
||||
:cgBIBtnMap="cgBIBtnMap"
|
||||
:buttonSwitch="buttonSwitch"
|
||||
:confirmBtnCfg="getFormConfirmButtonCfg"
|
||||
@success="reload"
|
||||
@formConfig="handleFormConfig"
|
||||
/>
|
||||
|
||||
<!-- 详情弹框 -->
|
||||
<online-detail-modal :id="ID" @register="registerDetailModal" />
|
||||
|
||||
<!-- 导入 -->
|
||||
<JImportModal @register="registerImportModal" :url="importUrl()" @ok="reload" online></JImportModal>
|
||||
|
||||
<!-- 跳转Href的动态组件方式 -->
|
||||
<a-modal v-bind="hrefComponent.model" v-on="hrefComponent.on">
|
||||
<component :is="hrefComponent.is" v-bind="hrefComponent.params" />
|
||||
</a-modal>
|
||||
|
||||
<!-- 自定义弹窗 -->
|
||||
<online-custom-modal @register="registerCustomModal" @success="reload" />
|
||||
|
||||
<!-- 弹窗给href到另外一张表单用-详情表单 -->
|
||||
<online-detail-modal :id="hrefMainTableId" @register="registerOnlineHrefModal" :defaultFullscreen="false" />
|
||||
|
||||
<!-- 弹窗到另外一张表单用-可编辑表单-关联记录的字段可在列表上打开modal编辑数据 -->
|
||||
<online-pop-modal ref="onlinePopModalRef" :id="popTableId" @register="registerPopModal" @success="reload" request topTip></online-pop-modal>
|
||||
|
||||
<!-- 流程图查看modal -->
|
||||
<BpmGraphicModal @register="registerBpmModal"></BpmGraphicModal>
|
||||
<!-- 页面loading[主要为了js增强使用] -->
|
||||
<Loading :loading="pageLoading" :absolute="true" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="CgformErpList">
|
||||
import { ref, nextTick, computed, watchEffect } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import OnlineAutoModal from '../default/OnlineAutoModal.vue';
|
||||
import OnlineCustomModal from '../default/OnlineCustomModal.vue';
|
||||
import OnlineDetailModal from '../default/OnlineDetailModal.vue';
|
||||
import { watch } from 'vue';
|
||||
import JImportModal from '/@/components/Form/src/jeecg/components/JImportModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useTableColumns } from '../../hooks/auto/useTableColumns';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import OnlineQueryForm from '../comp/OnlineQueryForm.vue';
|
||||
import OnlineSuperQuery from '../comp/superquery/SuperQuery.vue';
|
||||
import { useOnlineListPopEvent } from '../../hooks/auto/useOnlinePopEvent';
|
||||
import OnlinePopModal from '../comp/OnlinePopModal.vue';
|
||||
import OnlCgformErpSubTable from './OnlCgformErpSubTable.vue';
|
||||
import { ERP } from "../../util/constant";
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { Loading } from '/@/components/Loading';
|
||||
import { isObject } from '/@/utils/is';
|
||||
|
||||
const subTableData = ref([]);
|
||||
const tabActiveKey = ref(0);
|
||||
const selectedRowRcord = ref(null);
|
||||
const tableSetting = ref({});
|
||||
const { createMessage: $message } = useMessage();
|
||||
const cgformErpListRef = ref(null);
|
||||
const onlineTable = ref(null);
|
||||
// update-begin--author:liaozhiyang---date:20231128---for:【QQYUN-7260】erp主表编辑时保存子表记录
|
||||
const erpAllSubTableSource = ref({});
|
||||
const getSource = (tableName, data) => {
|
||||
erpAllSubTableSource.value[tableName] = data;
|
||||
};
|
||||
// update-end--author:liaozhiyang---date:20231128---for:【QQYUN-7260】erp主表编辑时保存子表记录
|
||||
// 这行代码应该在每次进入新的路由都会走,不管该路由有没有被缓存--
|
||||
const {
|
||||
ID,
|
||||
onlineTableContext,
|
||||
onlineQueryFormOuter,
|
||||
loading,
|
||||
reload,
|
||||
dataSource,
|
||||
pagination,
|
||||
handleSpecialConfig,
|
||||
getColumnList,
|
||||
handleChangeInTable,
|
||||
loadData,
|
||||
superQueryButtonRef,
|
||||
superQueryStatus,
|
||||
handleSuperQuery,
|
||||
onlineExtConfigJson,
|
||||
handleFormConfig,
|
||||
registerCustomModal,
|
||||
tableReloading,
|
||||
pageLoading,
|
||||
} = useOnlineTableContext({ themeTemplate: ERP });
|
||||
|
||||
const scroll = computed(() => {
|
||||
const reuslt = { y: 300 };
|
||||
if (isObject(tableScroll.value)) {
|
||||
return { ...tableScroll.value, ...reuslt };
|
||||
}
|
||||
return reuslt;
|
||||
});
|
||||
|
||||
// 监听数据源变化
|
||||
watch(dataSource, (value) => {
|
||||
if (selectedKeys.value.length > 0) {
|
||||
// 【TV360X-2701】选中的数据在当前页数据中不存在就清空选中
|
||||
selectedKeys.value = selectedKeys.value.filter((key) => value.some((item) => item['jeecg_row_key'] === key));
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20250722---for:【issues/8575】erp默认选中第一个及没选中主表时子表不查询
|
||||
if (pagination.value.current == 1 && selectedKeys.value.length === 0) {
|
||||
setTimeout(() => {
|
||||
const tableTbodyElem = document.querySelector('.ant-table-wrapper .ant-table-tbody');
|
||||
tableTbodyElem?.querySelector('.ant-table-row')?.click();
|
||||
}, 100);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20250722---for:【issues/8575】erp默认选中第一个及没选中主表时子表不查询
|
||||
});
|
||||
|
||||
// 判断 若ID不存在就终止后续逻辑
|
||||
if (!ID.value) {
|
||||
$message.warning('地址错误, 配置ID不存在!');
|
||||
// update-end--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
throw new Error('地址错误, 配置ID不存在!');
|
||||
// update-end--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
|
||||
}
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
// 处理列表button
|
||||
const {
|
||||
buttonSwitch,
|
||||
cgLinkButtonList,
|
||||
cgBIBtnMap,
|
||||
getQueryButtonCfg,
|
||||
getResetButtonCfg,
|
||||
getFormConfirmButtonCfg,
|
||||
cgTopButtonList,
|
||||
importUrl,
|
||||
registerModal,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
handleBatchDelete,
|
||||
registerImportModal,
|
||||
onImportExcel,
|
||||
onExportExcel,
|
||||
cgButtonJsHandler,
|
||||
cgButtonActionHandler,
|
||||
cgButtonLinkHandler,
|
||||
handleSubmitFlow,
|
||||
getDropDownActions,
|
||||
getActions,
|
||||
initButtonList,
|
||||
initButtonSwitch,
|
||||
registerDetailModal,
|
||||
registerBpmModal,
|
||||
} = useListButton(onlineTableContext, onlineExtConfigJson, {
|
||||
singleDelCallback: (id) => {
|
||||
// 删除的正好是选中的数据需清除选中的key
|
||||
if (Array.isArray(onlineTableContext['selectedRowKeys']) && onlineTableContext['selectedRowKeys'].includes(id)) {
|
||||
onlineTableContext['clearSelectedRow']();
|
||||
}
|
||||
},
|
||||
editClickCallback: (id, e) => {
|
||||
// 当前数据被选中了就得阻止冒泡,防止反选
|
||||
if (Array.isArray(onlineTableContext['selectedRowKeys']) && onlineTableContext['selectedRowKeys'].includes(id)) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
});
|
||||
const exportLoading = ref(false);
|
||||
|
||||
// 重写导出方法,防止频繁点击
|
||||
async function onExportExcelOverride() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
await onExportExcel();
|
||||
} finally {
|
||||
// 防止频繁点击,延迟1.5s关闭loading
|
||||
setTimeout(() => (exportLoading.value = false), 1500);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 BasicTable 的配置
|
||||
const {
|
||||
columns,
|
||||
actionColumn,
|
||||
selectedKeys,
|
||||
rowSelection,
|
||||
enableScrollBar,
|
||||
tableScroll,
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
handleColumnResult,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
hrefMainTableId,
|
||||
registerOnlineHrefModal,
|
||||
registerPopModal,
|
||||
openPopModal,
|
||||
onlinePopModalRef,
|
||||
popTableId,
|
||||
handleClickFieldHref,
|
||||
} = useTableColumns(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
watch(
|
||||
selectedKeys,
|
||||
(value) => {
|
||||
if (selectedKeys.value?.length) {
|
||||
selectedRowRcord.value = dataSource.value.find((item) => item['id'] === selectedKeys.value[0]);
|
||||
}else{
|
||||
selectedRowRcord.value = null;
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
// 监听表单配置ID
|
||||
watch(
|
||||
ID,
|
||||
() => {
|
||||
console.log('watched id is change...');
|
||||
initAutoList();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**重新加载online配置*/
|
||||
async function initAutoList() {
|
||||
loading.value = true;
|
||||
// 1.列配置信息
|
||||
let columnResult = await getColumnList(ERP);
|
||||
handleTableConfig(columnResult.main);
|
||||
subTableData.value = columnResult.subList;
|
||||
// 2.加载数据
|
||||
await loadData();
|
||||
loading.value = false;
|
||||
// 3.执行js增强 setup
|
||||
onlineTableContext.execButtonEnhance('setup');
|
||||
}
|
||||
|
||||
// 将查询结果转成 table渲染需要的配置
|
||||
function handleTableConfig(result) {
|
||||
// js增强初始化
|
||||
let EnhanceJS = initCgEnhanceJs(result.enhanceJs);
|
||||
onlineTableContext['EnhanceJS'] = EnhanceJS;
|
||||
// 自定义按钮设置
|
||||
initButtonList(result.cgButtonList);
|
||||
// 页面按钮显示隐藏状态设置
|
||||
initButtonSwitch(result.hideColumns);
|
||||
// 列配置
|
||||
handleColumnResult(result, 'radio');
|
||||
// 表配置
|
||||
handleSpecialConfig(result);
|
||||
// update-begin--author:liaozhiyang---date:20240611---for:【TV360X-1004】erp风格列设置之后再次刷新展示内容不对
|
||||
tableSetting.value = {
|
||||
cacheKey: `online_erp_mainTable_${result.currentTableName}`,
|
||||
};
|
||||
// update-end--author:liaozhiyang---date:20240611---for:【TV360X-1004】erp风格列设置之后再次刷新展示内容不对
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询控件 事件-执行查询
|
||||
* @param data
|
||||
*/
|
||||
function queryWithCondition(data) {
|
||||
onlineTableContext['queryParam'] = data;
|
||||
reload({mode:'search'});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询组件加载完成事件,获取高级查询需要的字段信息
|
||||
*/
|
||||
async function onQueryFormLoaded(json = {}) {
|
||||
console.log('onQueryFormLoaded', json);
|
||||
await getRefPromise(superQueryButtonRef);
|
||||
// update-begin--author:liaozhiyang---date:20230904---for:【QQYUN-6425】erp风格 高级查询 未查询出数据时 子表数据未清空
|
||||
const cJson = cloneDeep(json),
|
||||
{ properties = {} } = cJson;
|
||||
Object.entries(properties).forEach(([key, item]) => {
|
||||
if (item.view == 'table') {
|
||||
delete properties[key];
|
||||
}
|
||||
});
|
||||
superQueryButtonRef.value.init(cJson);
|
||||
// update-end--author:liaozhiyang---date:20230904---for:【QQYUN-6425】erp风格 高级查询 未查询出数据时 子表数据未清空
|
||||
}
|
||||
|
||||
/**
|
||||
* list页面打开 其他表单弹窗
|
||||
* @param params
|
||||
*/
|
||||
function openOnlinePopModal(params) {
|
||||
console.log('openOnlinePopModal', params);
|
||||
popTableId.value = params.id;
|
||||
let data = {
|
||||
title: params.describe,
|
||||
};
|
||||
if (params.record && params.record.id) {
|
||||
data['record'] = params.record;
|
||||
data['isUpdate'] = true;
|
||||
}
|
||||
openPopModal(true, data);
|
||||
}
|
||||
//绑定弹窗事件
|
||||
useOnlineListPopEvent(openOnlinePopModal);
|
||||
console.log('111111111-------onlineTableContext====111111111111111111', onlineTableContext);
|
||||
|
||||
const handleSuperSearch = (params, matchType) => {
|
||||
handleSuperQuery(params, matchType);
|
||||
selectedKeys.value = [];
|
||||
};
|
||||
// 解决erp主表5条时在笔记本上会出现滚动条问题
|
||||
watchEffect(() => {
|
||||
const size = onlineTable?.value?.getBindValuesRef().value?.size
|
||||
setTimeout(() => {
|
||||
const tableTbodyElem = cgformErpListRef.value.querySelector('.ant-table-wrapper .ant-table-body');
|
||||
if (tableTbodyElem) {
|
||||
tableTbodyElem.style.height = 'auto';
|
||||
if (dataSource.value.length > 0) {
|
||||
if (tableTbodyElem.offsetHeight < 300) {
|
||||
tableTbodyElem.style.height = `${tableTbodyElem.offsetHeight + 1}px`;
|
||||
console.log(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}, {
|
||||
flush: 'post'
|
||||
});
|
||||
// 1引入了loadsh console.log(that.simpleDateFormat(new Date().getTime(),'yyyy-MM-dd'));
|
||||
// 2. value的问题
|
||||
// 3. 变量位置改变后需要 重写api
|
||||
// 1添加按钮的时候 预留出样式对象 然后js增强中设置样式对象
|
||||
// 2直接设置css字符串 然后通过js document 往head里面增加css片段 全局生效
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
// update-begin--author:liaozhiyang---date:20240313---for:【QQYUN-8493】修正暗黑模式online表单Erp和编辑页面显示不正确
|
||||
html[data-theme='light'] {
|
||||
.cgformErpList {
|
||||
height: 100%;
|
||||
.content {
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240313---for:【QQYUN-8493】修正暗黑模式online表单Erp和编辑页面显示不正确
|
||||
/** [表格主题样式一] 表格强制列不换行 */
|
||||
.j-table-force-nowrap {
|
||||
td,
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ant-table-selection-column {
|
||||
padding: 12px 22px !important;
|
||||
}
|
||||
|
||||
/** 列自适应,弊端会导致列宽失效 */
|
||||
&.ant-table-wrapper .ant-table-content {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
.online-cell-image {
|
||||
height: 25px !important;
|
||||
margin: 0 auto;
|
||||
max-width: 80px;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
// 为了解决笔记本上计算出浮点数导致5条也出现滚动条
|
||||
:deep(.ant-table-body) {
|
||||
line-height: 22px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,403 @@
|
||||
<template>
|
||||
|
||||
<!-- 加载中骨架屏 -->
|
||||
<a-skeleton v-if="tableReloading" active />
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<online-query-form
|
||||
v-if="mainTableSelectedRowRcord"
|
||||
v-show="!tableReloading"
|
||||
ref="onlineQueryFormOuter"
|
||||
:id="ID"
|
||||
:queryBtnCfg="getQueryButtonCfg"
|
||||
:resetBtnCfg="getResetButtonCfg"
|
||||
@search="queryWithCondition"
|
||||
/>
|
||||
|
||||
<!-- 列表 -->
|
||||
<BasicTable
|
||||
v-if="!tableReloading"
|
||||
ref="onlineTable"
|
||||
rowKey="jeecg_row_key"
|
||||
:canResize="false"
|
||||
:bordered="true"
|
||||
:showIndexColumn="false"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="pagination"
|
||||
:rowSelection="rowSelection"
|
||||
:actionColumn="actionColumn"
|
||||
:showTableSetting="true"
|
||||
:clickToRowSelect="false"
|
||||
:scroll="tableScroll"
|
||||
@table-redo="reload"
|
||||
:class="{ 'j-table-force-nowrap': enableScrollBar }"
|
||||
:tableSetting="tableSetting"
|
||||
@change="handleChangeInTable"
|
||||
:minHeight="300"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
v-if="btnShow && buttonSwitch.add && cgBIBtnMap['add'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['add'].buttonIcon"
|
||||
@click="handleAddRcord"
|
||||
>
|
||||
<span>{{cgBIBtnMap['add'].buttonName}}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="btnShow && buttonSwitch.import && cgBIBtnMap['import'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['import'].buttonIcon"
|
||||
@click="onImportExcel"
|
||||
>
|
||||
<span>{{cgBIBtnMap['import'].buttonName}}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="btnShow && buttonSwitch.export && cgBIBtnMap['export'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['export'].buttonIcon"
|
||||
:loading="exportLoading"
|
||||
@click="onExportExcelOverride"
|
||||
>
|
||||
<span>{{cgBIBtnMap['export'].buttonName}}</span>
|
||||
</a-button>
|
||||
|
||||
<!-- 自定义按钮 -->
|
||||
<template v-if="btnShow && cgTopButtonList && cgTopButtonList.length > 0" v-for="(item, index) in cgTopButtonList">
|
||||
<a-button
|
||||
v-if="item.optType == 'js'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonJsHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-else-if="item.optType == 'action'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonActionHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<a-button
|
||||
v-show="selectedKeys.length > 0"
|
||||
v-if="buttonSwitch.batch_delete && cgBIBtnMap['batch_delete'].enabled"
|
||||
:preIcon="cgBIBtnMap['batch_delete'].buttonIcon"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
<span>{{cgBIBtnMap['batch_delete'].buttonName}}</span>
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #fileSlot="{ text, record, column }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
|
||||
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download" size="small" @click="downloadRowFile(text, record, column, ID)">
|
||||
下载
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #imgSlot="{ text }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
|
||||
<img v-else :src="getImgView(text)" alt="图片不存在" class="online-cell-image" @click="viewOnlineCellImage(text)" />
|
||||
</template>
|
||||
|
||||
<template #htmlSlot="{ text, column, record }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
<template v-if="column.fieldHref">
|
||||
<a v-html="text" @click="handleClickFieldHref(column.fieldHref, record)"></a>
|
||||
</template>
|
||||
<div v-else v-html="text"></div>
|
||||
<!-- update-end--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
</template>
|
||||
|
||||
<template #pcaSlot="{ text, column }">
|
||||
<div :title="getPcaText(text, column)">{{ getPcaText(text, column) }}</div>
|
||||
</template>
|
||||
|
||||
<template #dateSlot="{ text, column }">
|
||||
<span>{{ getFormatDate(text, column) }}</span>
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 表单新增、修改弹框 -->
|
||||
<OnlineAutoModal
|
||||
@register="registerModal"
|
||||
:id="ID"
|
||||
:source="ERPSUBTABLE"
|
||||
:cgBIBtnMap="cgBIBtnMap"
|
||||
:buttonSwitch="buttonSwitch"
|
||||
:confirmBtnCfg="getFormConfirmButtonCfg"
|
||||
@success="reload"
|
||||
@formConfig="handleFormConfig"
|
||||
/>
|
||||
|
||||
<!-- 详情弹框 -->
|
||||
<online-detail-modal :source="ERPSUBTABLE" :id="ID" @register="registerDetailModal" />
|
||||
|
||||
<!-- 导入 -->
|
||||
<JImportModal @register="registerImportModal" :url="importUrl()" @ok="reload" online></JImportModal>
|
||||
|
||||
<!-- 跳转Href的动态组件方式 -->
|
||||
<a-modal v-bind="hrefComponent.model" v-on="hrefComponent.on">
|
||||
<component :is="hrefComponent.is" v-bind="hrefComponent.params" />
|
||||
</a-modal>
|
||||
|
||||
<!-- 自定义弹窗 -->
|
||||
<online-custom-modal @register="registerCustomModal" @success="reload" />
|
||||
|
||||
<!-- 弹窗给href到另外一张表单用-详情表单 -->
|
||||
<online-detail-modal :id="hrefMainTableId" @register="registerOnlineHrefModal" :defaultFullscreen="false" />
|
||||
|
||||
<!-- 弹窗到另外一张表单用-可编辑表单-关联记录的字段可在列表上打开modal编辑数据 -->
|
||||
<online-pop-modal ref="onlinePopModalRef" :id="popTableId" @register="registerPopModal" @success="reload" request topTip></online-pop-modal>
|
||||
|
||||
<!-- 流程图查看modal -->
|
||||
<BpmGraphicModal @register="registerBpmModal"></BpmGraphicModal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, provide } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import OnlineAutoModal from '../default/OnlineAutoModal.vue';
|
||||
import OnlineCustomModal from '../default/OnlineCustomModal.vue';
|
||||
import OnlineDetailModal from '../default/OnlineDetailModal.vue';
|
||||
import { watch } from 'vue';
|
||||
import JImportModal from '/@/components/Form/src/jeecg/components/JImportModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useTableColumns } from '../../hooks/auto/useTableColumns';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import OnlineQueryForm from '../comp/OnlineQueryForm.vue';
|
||||
import { useOnlineListPopEvent } from '../../hooks/auto/useOnlinePopEvent';
|
||||
import OnlinePopModal from '../comp/OnlinePopModal.vue';
|
||||
import { ERP, ERPSUBTABLE } from '../../util/constant';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
const props = defineProps(['data', 'mainTableSelectedRowRcord']);
|
||||
const emit = defineEmits(['getSource']);
|
||||
const btnShow = ref(false);
|
||||
// update-begin--author:liaozhiyang---date:20240611---for:【TV360X-1004】erp风格列设置之后再次刷新展示内容不对
|
||||
const tableSetting = {
|
||||
cacheKey: `online_erp_subTable_${props.data.currentTableName}`,
|
||||
};
|
||||
// update-end--author:liaozhiyang---date:20240611---for:【TV360X-1004】erp风格列设置之后再次刷新展示内容不对
|
||||
const { createMessage: $message } = useMessage();
|
||||
// 这行代码应该在每次进入新的路由都会走,不管该路由有没有被缓存--
|
||||
const {
|
||||
ID,
|
||||
onlineTableContext,
|
||||
onlineQueryFormOuter,
|
||||
loading,
|
||||
reload,
|
||||
dataSource,
|
||||
pagination,
|
||||
handleSpecialConfig,
|
||||
getColumnList,
|
||||
handleChangeInTable,
|
||||
loadData,
|
||||
onlineExtConfigJson,
|
||||
handleFormConfig,
|
||||
registerCustomModal,
|
||||
tableReloading,
|
||||
} = useOnlineTableContext({ code: props.data.code, themeTemplate: ERP });
|
||||
onlineTableContext['isErpSubTable'] = true;
|
||||
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
// 处理列表button
|
||||
const {
|
||||
buttonSwitch,
|
||||
cgLinkButtonList,
|
||||
cgBIBtnMap,
|
||||
getQueryButtonCfg,
|
||||
getResetButtonCfg,
|
||||
getFormConfirmButtonCfg,
|
||||
cgTopButtonList,
|
||||
importUrl,
|
||||
registerModal,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
handleBatchDelete,
|
||||
registerImportModal,
|
||||
onImportExcel,
|
||||
onExportExcel,
|
||||
cgButtonJsHandler,
|
||||
cgButtonActionHandler,
|
||||
cgButtonLinkHandler,
|
||||
handleSubmitFlow,
|
||||
getDropDownActions,
|
||||
getActions,
|
||||
initButtonList,
|
||||
initButtonSwitch,
|
||||
registerDetailModal,
|
||||
registerBpmModal,
|
||||
} = useListButton(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
const exportLoading = ref(false);
|
||||
|
||||
// 重写导出方法,防止频繁点击
|
||||
async function onExportExcelOverride() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
await onExportExcel();
|
||||
} finally {
|
||||
// 防止频繁点击,延迟1.5s关闭loading
|
||||
setTimeout(() => (exportLoading.value = false), 1500);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 BasicTable 的配置
|
||||
const {
|
||||
columns,
|
||||
actionColumn,
|
||||
selectedKeys,
|
||||
rowSelection,
|
||||
enableScrollBar,
|
||||
tableScroll,
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
handleColumnResult,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
hrefMainTableId,
|
||||
registerOnlineHrefModal,
|
||||
registerPopModal,
|
||||
openPopModal,
|
||||
onlinePopModalRef,
|
||||
popTableId,
|
||||
handleClickFieldHref,
|
||||
} = useTableColumns(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
initAutoList(cloneDeep(props.data));
|
||||
|
||||
const foreignkeyRef = ref(null);
|
||||
const foreignKeys = props.data.foreignKeys;
|
||||
let $key;
|
||||
if (foreignKeys?.length) {
|
||||
// 外键只有一个,但是给的是数组形式,只取第一个即可.
|
||||
const item = foreignKeys[0];
|
||||
const field = item.field;
|
||||
$key = item.key;
|
||||
onlineTableContext['foreignKeyField'] = field;
|
||||
} else {
|
||||
onlineTableContext['foreignKeyField'] = null;
|
||||
onlineTableContext['foreignKeyValue'] = null;
|
||||
}
|
||||
provide('foreignkey', foreignkeyRef);
|
||||
watch(
|
||||
() => props.mainTableSelectedRowRcord,
|
||||
(newVal) => {
|
||||
// 切换重置状态
|
||||
pagination.value.current = 1;
|
||||
selectedKeys.value = [];
|
||||
// update-begin--author:liaozhiyang---date:20240523---for:【TV360X-124】erp风格,切换主表数据时,子表查询条件未清空
|
||||
onlineQueryFormOuter.value?.clearSearch();
|
||||
// update-end--author:liaozhiyang---date:20240523---for:【TV360X-124】erp风格,切换主表数据时,子表查询条件未清空
|
||||
if (newVal) {
|
||||
if (onlineTableContext['foreignKeyField']) {
|
||||
const value = newVal[$key];
|
||||
onlineTableContext['foreignKeyValue'] = value;
|
||||
foreignkeyRef.value = { field: onlineTableContext['foreignKeyField'], value };
|
||||
}
|
||||
btnShow.value = true;
|
||||
loading.value = true;
|
||||
loadData().finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
} else {
|
||||
btnShow.value = false;
|
||||
dataSource.value = [];
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
// update-begin--author:liaozhiyang---date:20231128---for:【QQYUN-7260】erp主表编辑时保存子表记录
|
||||
watch(
|
||||
() => dataSource.value,
|
||||
() => {
|
||||
emit('getSource', props.data.currentTableName, dataSource.value);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
// update-end--author:liaozhiyang---date:20231128---for:【QQYUN-7260】erp主表编辑时保存子表记录
|
||||
/**重新加载online配置*/
|
||||
async function initAutoList(data) {
|
||||
// 1.列配置信息
|
||||
handleTableConfig(data);
|
||||
loading.value = false;
|
||||
// 2.加载数据
|
||||
// await loadData();
|
||||
// loading.value = false;
|
||||
// 3.执行js增强 setup
|
||||
onlineTableContext.execButtonEnhance('setup');
|
||||
}
|
||||
|
||||
// 将查询结果转成 table渲染需要的配置
|
||||
function handleTableConfig(result) {
|
||||
// js增强初始化
|
||||
let EnhanceJS = initCgEnhanceJs(result.enhanceJs);
|
||||
onlineTableContext['EnhanceJS'] = EnhanceJS;
|
||||
// 自定义按钮设置
|
||||
initButtonList(result.cgButtonList);
|
||||
// 页面按钮显示隐藏状态设置
|
||||
initButtonSwitch(result.hideColumns);
|
||||
// 列配置
|
||||
handleColumnResult(result);
|
||||
// 表配置
|
||||
handleSpecialConfig(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询控件 事件-执行查询
|
||||
* @param data
|
||||
*/
|
||||
function queryWithCondition(data) {
|
||||
onlineTableContext['queryParam'] = data;
|
||||
reload({mode:'search'});
|
||||
}
|
||||
|
||||
/**
|
||||
* list页面打开 其他表单弹窗
|
||||
* @param params
|
||||
*/
|
||||
function openOnlinePopModal(params) {
|
||||
console.log('openOnlinePopModal', params);
|
||||
popTableId.value = params.id;
|
||||
let data = {
|
||||
title: params.describe,
|
||||
};
|
||||
if (params.record && params.record.id) {
|
||||
data['record'] = params.record;
|
||||
data['isUpdate'] = true;
|
||||
}
|
||||
openPopModal(true, data);
|
||||
}
|
||||
//绑定弹窗事件
|
||||
useOnlineListPopEvent(openOnlinePopModal);
|
||||
const handleAddRcord = () => {
|
||||
if(props.data.relationType ==1 && dataSource.value.length) {
|
||||
$message.warning('一对一的表只能新增一条数据');
|
||||
} else {
|
||||
handleAdd();
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -0,0 +1,246 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<BasicTable
|
||||
v-if="!tableReloading"
|
||||
ref="onlineTable"
|
||||
rowKey="jeecg_row_key"
|
||||
:canResize="true"
|
||||
:bordered="true"
|
||||
:showIndexColumn="false"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="false"
|
||||
:showActionColumn="false"
|
||||
:showTableSetting="false"
|
||||
:clickToRowSelect="false"
|
||||
:scroll="tableScroll"
|
||||
@table-redo="reload"
|
||||
:class="{ 'j-table-force-nowrap': enableScrollBar }"
|
||||
@change="handleChangeInTable"
|
||||
>
|
||||
<template #fileSlot="{ text, record, column }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
|
||||
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download" size="small" @click="downloadRowFile(text, record, column, ID)">
|
||||
下载
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #imgSlot="{ text }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
|
||||
<img v-else :src="getImgView(text)" alt="图片不存在" class="online-cell-image" @click="viewOnlineCellImage(text)" />
|
||||
</template>
|
||||
|
||||
<template #htmlSlot="{ text, column, record }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
<template v-if="column.fieldHref">
|
||||
<a v-html="text" @click="handleClickFieldHref(column.fieldHref, record)"></a>
|
||||
</template>
|
||||
<div v-else v-html="text"></div>
|
||||
<!-- update-end--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
</template>
|
||||
|
||||
<template #pcaSlot="{ text, column }">
|
||||
<div :title="getPcaText(text, column)">{{ getPcaText(text, column) }}</div>
|
||||
</template>
|
||||
|
||||
<template #dateSlot="{ text, column }">
|
||||
<span>{{ getFormatDate(text, column) }}</span>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
|
||||
<!-- 详情弹框 -->
|
||||
<online-detail-modal :id="ID" @register="registerDetailModal" :source="INNER_TABLE"/>
|
||||
|
||||
<!-- 导入 -->
|
||||
<JImportModal @register="registerImportModal" :url="importUrl()" @ok="reload" online></JImportModal>
|
||||
|
||||
<!-- 跳转Href的动态组件方式 -->
|
||||
<a-modal v-bind="hrefComponent.model" v-on="hrefComponent.on">
|
||||
<component :is="hrefComponent.is" v-bind="hrefComponent.params" />
|
||||
</a-modal>
|
||||
|
||||
<!-- 自定义弹窗 -->
|
||||
<online-custom-modal @register="registerCustomModal" @success="reload" />
|
||||
|
||||
<!-- 弹窗给href到另外一张表单用-详情表单 -->
|
||||
<online-detail-modal :id="hrefMainTableId" @register="registerOnlineHrefModal" :defaultFullscreen="false" :source="INNER_TABLE" />
|
||||
|
||||
<!-- 弹窗到另外一张表单用-可编辑表单-关联记录的字段可在列表上打开modal编辑数据 -->
|
||||
<online-pop-modal ref="onlinePopModalRef" :id="popTableId" @register="registerPopModal" @success="reload" request topTip></online-pop-modal>
|
||||
|
||||
<!-- 流程图查看modal -->
|
||||
<BpmGraphicModal @register="registerBpmModal"></BpmGraphicModal>
|
||||
</template>
|
||||
|
||||
<script setup name="OnlCgformInnerSubTable">
|
||||
import { ref, watch } from 'vue';
|
||||
import { BasicTable } from '/@/components/Table';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import OnlineCustomModal from '../default/OnlineCustomModal.vue';
|
||||
import OnlineDetailModal from '../default/OnlineDetailModal.vue';
|
||||
import JImportModal from '/@/components/Form/src/jeecg/components/JImportModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useTableColumns } from '../../hooks/auto/useTableColumns';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { useOnlineListPopEvent } from '../../hooks/auto/useOnlinePopEvent';
|
||||
import OnlinePopModal from '../comp/OnlinePopModal.vue';
|
||||
import { INNER_TABLE } from '../../util/constant';
|
||||
import { ACTION_COLUMN_FLAG } from '/@/components/Table/src/const';
|
||||
|
||||
/**
|
||||
1.内嵌子表单没有分页
|
||||
2.内嵌子表单没有查询区域
|
||||
3.内嵌子表单没有导出导入等按钮
|
||||
4.查询始终都是主表数据id(外键不起作用)
|
||||
5.内嵌子表单没有新增和编辑,没有操作列
|
||||
*/
|
||||
const props = defineProps(['subTableId', 'subTableName', 'mTableSelectedRcordId']);
|
||||
const { createMessage: $message } = useMessage();
|
||||
// 这行代码应该在每次进入新的路由都会走,不管该路由有没有被缓存--
|
||||
const {
|
||||
ID,
|
||||
onlineTableContext,
|
||||
loading,
|
||||
reload,
|
||||
dataSource,
|
||||
handleSpecialConfig,
|
||||
getColumnList,
|
||||
handleChangeInTable,
|
||||
loadData,
|
||||
onlineExtConfigJson,
|
||||
registerCustomModal,
|
||||
tableReloading,
|
||||
} = useOnlineTableContext({ code: props.subTableId });
|
||||
onlineTableContext['isInnerSubTable'] = true;
|
||||
onlineTableContext['innerSubTableName'] = props.subTableName;
|
||||
onlineTableContext['innerSubTableId'] = ID.value;
|
||||
onlineTableContext['mTableSelectedRcordId'] = props.mTableSelectedRcordId;
|
||||
|
||||
// 判断 若ID不存在就终止后续逻辑
|
||||
if (!ID.value) {
|
||||
$message.warning('地址错误, 配置ID不存在!');
|
||||
}
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
// 处理列表button
|
||||
const {
|
||||
importUrl,
|
||||
registerImportModal,
|
||||
initButtonList,
|
||||
initButtonSwitch,
|
||||
registerDetailModal,
|
||||
registerBpmModal,
|
||||
} = useListButton(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
|
||||
// 处理 BasicTable 的配置
|
||||
const {
|
||||
columns,
|
||||
enableScrollBar,
|
||||
tableScroll,
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
handleColumnResult,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
hrefMainTableId,
|
||||
registerOnlineHrefModal,
|
||||
registerPopModal,
|
||||
openPopModal,
|
||||
onlinePopModalRef,
|
||||
popTableId,
|
||||
handleClickFieldHref,
|
||||
} = useTableColumns(onlineTableContext, onlineExtConfigJson);
|
||||
// 监听表单配置ID
|
||||
watch(
|
||||
ID,
|
||||
() => {
|
||||
console.log('watched id is change...');
|
||||
initAutoList();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
/**重新加载online配置*/
|
||||
async function initAutoList() {
|
||||
loading.value = true;
|
||||
// 1.列配置信息
|
||||
let columnResult = await getColumnList(INNER_TABLE);
|
||||
handleTableConfig(columnResult);
|
||||
// update-begin--author:liaozhiyang---date:20240514---for:【QQYUN-9340】内嵌子表数据都查出来了
|
||||
if (columnResult.foreignKeys?.length) {
|
||||
onlineTableContext['innerSubTableFk'] = columnResult.foreignKeys[0].field;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240514---for:【QQYUN-9340】内嵌子表数据都查出来了
|
||||
// 2.加载数据
|
||||
await loadData();
|
||||
loading.value = false;
|
||||
// 3.执行js增强 setup
|
||||
onlineTableContext.execButtonEnhance('setup');
|
||||
}
|
||||
|
||||
// 将查询结果转成 table渲染需要的配置
|
||||
function handleTableConfig(result) {
|
||||
// js增强初始化
|
||||
let EnhanceJS = initCgEnhanceJs(result.enhanceJs);
|
||||
onlineTableContext['EnhanceJS'] = EnhanceJS;
|
||||
// 自定义按钮设置
|
||||
initButtonList(result.cgButtonList);
|
||||
// 页面按钮显示隐藏状态设置
|
||||
initButtonSwitch(result.hideColumns);
|
||||
// 列配置
|
||||
handleColumnResult(result);
|
||||
// 表配置
|
||||
handleSpecialConfig(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* list页面打开 其他表单弹窗
|
||||
* @param params
|
||||
*/
|
||||
function openOnlinePopModal(params) {
|
||||
console.log('openOnlinePopModal', params);
|
||||
popTableId.value = params.id;
|
||||
let data = {
|
||||
title: params.describe,
|
||||
};
|
||||
if (params.record && params.record.id) {
|
||||
data['record'] = params.record;
|
||||
data['isUpdate'] = true;
|
||||
}
|
||||
openPopModal(true, data);
|
||||
}
|
||||
//绑定弹窗事件
|
||||
useOnlineListPopEvent(openOnlinePopModal);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
/** [表格主题样式一] 表格强制列不换行 */
|
||||
.j-table-force-nowrap {
|
||||
td,
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ant-table-selection-column {
|
||||
padding: 12px 22px !important;
|
||||
}
|
||||
|
||||
/** 列自适应,弊端会导致列宽失效 */
|
||||
&.ant-table-wrapper .ant-table-content {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
.online-cell-image {
|
||||
height: 25px !important;
|
||||
margin: 0 auto;
|
||||
max-width: 80px;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,458 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
|
||||
<!-- 加载中骨架屏 -->
|
||||
<a-skeleton v-if="tableReloading" active />
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<online-query-form
|
||||
v-show="!tableReloading"
|
||||
ref="onlineQueryFormOuter"
|
||||
:id="ID"
|
||||
:queryBtnCfg="getQueryButtonCfg"
|
||||
:resetBtnCfg="getResetButtonCfg"
|
||||
@search="queryWithCondition"
|
||||
@loaded="onQueryFormLoaded"
|
||||
/>
|
||||
|
||||
<!-- 列表 -->
|
||||
<BasicTable
|
||||
v-if="!tableReloading"
|
||||
ref="onlineTable"
|
||||
rowKey="jeecg_row_key"
|
||||
:canResize="true"
|
||||
:bordered="true"
|
||||
:showIndexColumn="false"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="pagination"
|
||||
:rowSelection="rowSelection"
|
||||
:actionColumn="actionColumn"
|
||||
:showTableSetting="true"
|
||||
:clickToRowSelect="false"
|
||||
:scroll="tableScroll"
|
||||
@table-redo="reload"
|
||||
:class="{ 'j-table-force-nowrap': enableScrollBar }"
|
||||
@change="handleChangeInTable"
|
||||
:expandedRowKeys="expandedRowKeys"
|
||||
@expand="handleExpand"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
v-if="buttonSwitch.add && cgBIBtnMap['add'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['add'].buttonIcon"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<span>{{cgBIBtnMap['add'].buttonName}}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="buttonSwitch.import && cgBIBtnMap['import'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['import'].buttonIcon"
|
||||
@click="onImportExcel"
|
||||
>
|
||||
<span>{{cgBIBtnMap['import'].buttonName}}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="buttonSwitch.export && cgBIBtnMap['export'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['export'].buttonIcon"
|
||||
:loading="exportLoading"
|
||||
@click="onExportExcelOverride"
|
||||
>
|
||||
<span>{{cgBIBtnMap['export'].buttonName}}</span>
|
||||
</a-button>
|
||||
|
||||
<!-- 自定义按钮 -->
|
||||
<template v-if="cgTopButtonList && cgTopButtonList.length > 0" v-for="(item, index) in cgTopButtonList">
|
||||
<a-button
|
||||
v-if="item.optType == 'js'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonJsHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-else-if="item.optType == 'action'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonActionHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<a-button
|
||||
v-show="selectedKeys.length > 0"
|
||||
v-if="buttonSwitch.batch_delete && cgBIBtnMap['batch_delete'].enabled"
|
||||
:preIcon="cgBIBtnMap['batch_delete'].buttonIcon"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
<span>{{cgBIBtnMap['batch_delete'].buttonName}}</span>
|
||||
</a-button>
|
||||
|
||||
<online-super-query
|
||||
v-if="buttonSwitch.super_query && cgBIBtnMap['super_query'].enabled"
|
||||
ref="superQueryButtonRef"
|
||||
online
|
||||
:status="superQueryStatus"
|
||||
:queryBtnCfg="cgBIBtnMap['super_query']"
|
||||
@search="handleSuperQuery"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 子表 -->
|
||||
<template #expandedRowRender="{ record }">
|
||||
<a-tabs v-model:activeKey="innerSubTable.tabIndex" v-if="expandedRowKeys[0] && record.id == expandedRowKeys[0]">
|
||||
<a-tab-pane v-if="expandedRowKeys.length" v-for="(item, index) in innerSubTable.tabNav" :tab="item.tableTxt" :key="index + ''">
|
||||
<OnlCgformInnerSubTable
|
||||
v-if="innerSubTable.tabIndex == index"
|
||||
:subTableId="item.id"
|
||||
:mTableSelectedRcordId="expandedRowKeys[0]"
|
||||
:subTableName="item.tableName"
|
||||
></OnlCgformInnerSubTable>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<template #fileSlot="{ text, record, column }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
|
||||
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download" size="small" @click="downloadRowFile(text, record, column, ID)">
|
||||
下载
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #imgSlot="{ text }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
|
||||
<img v-else :src="getImgView(text)" alt="图片不存在" class="online-cell-image" @click="viewOnlineCellImage(text)" />
|
||||
</template>
|
||||
|
||||
<template #htmlSlot="{ text, column, record }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
<template v-if="column.fieldHref">
|
||||
<a v-html="text" @click="handleClickFieldHref(column.fieldHref, record)"></a>
|
||||
</template>
|
||||
<div v-else v-html="text"></div>
|
||||
<!-- update-end--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
</template>
|
||||
|
||||
<template #pcaSlot="{ text, column }">
|
||||
<div :title="getPcaText(text, column)">{{ getPcaText(text, column) }}</div>
|
||||
</template>
|
||||
|
||||
<template #dateSlot="{ text, column }">
|
||||
<span>{{ getFormatDate(text, column) }}</span>
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 表单新增、修改弹框 -->
|
||||
<OnlineAutoModal
|
||||
@register="registerModal"
|
||||
:id="ID"
|
||||
:cgBIBtnMap="cgBIBtnMap"
|
||||
:buttonSwitch="buttonSwitch"
|
||||
:confirmBtnCfg="getFormConfirmButtonCfg"
|
||||
@success="hanldeSuccess"
|
||||
@formConfig="getSubColunm"
|
||||
/>
|
||||
|
||||
<!-- 详情弹框 -->
|
||||
<online-detail-modal :id="ID" @register="registerDetailModal" />
|
||||
|
||||
<!-- 导入 -->
|
||||
<JImportModal @register="registerImportModal" :url="importUrl()" @ok="reload" online></JImportModal>
|
||||
|
||||
<!-- 跳转Href的动态组件方式 -->
|
||||
<a-modal v-bind="hrefComponent.model" v-on="hrefComponent.on">
|
||||
<component :is="hrefComponent.is" v-bind="hrefComponent.params" />
|
||||
</a-modal>
|
||||
|
||||
<!-- 自定义弹窗 -->
|
||||
<online-custom-modal @register="registerCustomModal" @success="reload" />
|
||||
|
||||
<!-- 弹窗给href到另外一张表单用-详情表单 -->
|
||||
<online-detail-modal :id="hrefMainTableId" @register="registerOnlineHrefModal" :defaultFullscreen="false" />
|
||||
|
||||
<!-- 弹窗到另外一张表单用-可编辑表单-关联记录的字段可在列表上打开modal编辑数据 -->
|
||||
<online-pop-modal ref="onlinePopModalRef" :id="popTableId" @register="registerPopModal" @success="reload" request topTip></online-pop-modal>
|
||||
|
||||
<!-- 流程图查看modal -->
|
||||
<BpmGraphicModal @register="registerBpmModal"></BpmGraphicModal>
|
||||
<!-- 页面loading[主要为了js增强使用] -->
|
||||
<Loading :loading="pageLoading" :absolute="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="OnlCgformInnerTableList">
|
||||
import { ref, reactive, watch, nextTick } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import OnlineAutoModal from '../default/OnlineAutoModal.vue';
|
||||
import OnlineCustomModal from '../default/OnlineCustomModal.vue';
|
||||
import OnlineDetailModal from '../default/OnlineDetailModal.vue';
|
||||
import JImportModal from '/@/components/Form/src/jeecg/components/JImportModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useTableColumns } from '../../hooks/auto/useTableColumns';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import OnlineQueryForm from '../comp/OnlineQueryForm.vue';
|
||||
import OnlineSuperQuery from '../comp/superquery/SuperQuery.vue';
|
||||
import { useOnlineListPopEvent } from '../../hooks/auto/useOnlinePopEvent';
|
||||
import OnlinePopModal from '../comp/OnlinePopModal.vue';
|
||||
import OnlCgformInnerSubTable from './OnlCgformInnerSubTable.vue';
|
||||
import { INNER_TABLE } from '../../util/constant';
|
||||
import { Loading } from '/@/components/Loading';
|
||||
|
||||
const innerSubTable = reactive({
|
||||
tabNav: [],
|
||||
tabIndex: '0',
|
||||
});
|
||||
const expandedRowKeys = ref([]);
|
||||
const mainTableSelectedRowRcord = ref(null);
|
||||
const { createMessage: $message } = useMessage();
|
||||
// 这行代码应该在每次进入新的路由都会走,不管该路由有没有被缓存--
|
||||
const {
|
||||
ID,
|
||||
onlineTableContext,
|
||||
onlineQueryFormOuter,
|
||||
loading,
|
||||
reload,
|
||||
dataSource,
|
||||
pagination,
|
||||
handleSpecialConfig,
|
||||
getColumnList,
|
||||
handleChangeInTable,
|
||||
loadData,
|
||||
superQueryButtonRef,
|
||||
superQueryStatus,
|
||||
handleSuperQuery,
|
||||
onlineExtConfigJson,
|
||||
handleFormConfig,
|
||||
registerCustomModal,
|
||||
tableReloading,
|
||||
pageLoading,
|
||||
} = useOnlineTableContext();
|
||||
|
||||
// 判断 若ID不存在就终止后续逻辑
|
||||
if (!ID.value) {
|
||||
$message.warning('地址错误, 配置ID不存在!');
|
||||
// update-end--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
throw new Error('地址错误, 配置ID不存在!');
|
||||
// update-end--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
}
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
// 处理列表button
|
||||
const {
|
||||
buttonSwitch,
|
||||
cgBIBtnMap,
|
||||
getQueryButtonCfg,
|
||||
getResetButtonCfg,
|
||||
getFormConfirmButtonCfg,
|
||||
cgTopButtonList,
|
||||
importUrl,
|
||||
registerModal,
|
||||
handleAdd,
|
||||
handleBatchDelete,
|
||||
registerImportModal,
|
||||
onImportExcel,
|
||||
onExportExcel,
|
||||
cgButtonJsHandler,
|
||||
cgButtonActionHandler,
|
||||
getDropDownActions,
|
||||
getActions,
|
||||
initButtonList,
|
||||
initButtonSwitch,
|
||||
registerDetailModal,
|
||||
registerBpmModal,
|
||||
} = useListButton(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
const exportLoading = ref(false);
|
||||
|
||||
// 重写导出方法,防止频繁点击
|
||||
async function onExportExcelOverride() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
await onExportExcel();
|
||||
} finally {
|
||||
// 防止频繁点击,延迟1.5s关闭loading
|
||||
setTimeout(() => (exportLoading.value = false), 1500);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 BasicTable 的配置
|
||||
const {
|
||||
columns,
|
||||
actionColumn,
|
||||
selectedKeys,
|
||||
rowSelection,
|
||||
enableScrollBar,
|
||||
tableScroll,
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
handleColumnResult,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
hrefMainTableId,
|
||||
registerOnlineHrefModal,
|
||||
registerPopModal,
|
||||
openPopModal,
|
||||
onlinePopModalRef,
|
||||
popTableId,
|
||||
handleClickFieldHref,
|
||||
} = useTableColumns(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
// 监听表单配置ID
|
||||
watch(
|
||||
ID,
|
||||
() => {
|
||||
console.log('watched id is change...');
|
||||
initAutoList();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**重新加载online配置*/
|
||||
async function initAutoList() {
|
||||
loading.value = true;
|
||||
// 1.列配置信息
|
||||
let columnResult = await getColumnList(INNER_TABLE);
|
||||
handleTableConfig(columnResult);
|
||||
// 2.加载数据
|
||||
await loadData();
|
||||
loading.value = false;
|
||||
// 3.执行js增强 setup
|
||||
onlineTableContext.execButtonEnhance('setup');
|
||||
}
|
||||
|
||||
// 将查询结果转成 table渲染需要的配置
|
||||
function handleTableConfig(result) {
|
||||
// js增强初始化
|
||||
let EnhanceJS = initCgEnhanceJs(result.enhanceJs);
|
||||
onlineTableContext['EnhanceJS'] = EnhanceJS;
|
||||
// 自定义按钮设置
|
||||
initButtonList(result.cgButtonList);
|
||||
// 页面按钮显示隐藏状态设置
|
||||
initButtonSwitch(result.hideColumns);
|
||||
// 列配置
|
||||
handleColumnResult(result);
|
||||
// 表配置
|
||||
handleSpecialConfig(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询控件 事件-执行查询
|
||||
* @param data
|
||||
*/
|
||||
function queryWithCondition(data) {
|
||||
onlineTableContext['queryParam'] = data;
|
||||
reload({mode:'search'});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询组件加载完成事件,获取高级查询需要的字段信息
|
||||
*/
|
||||
async function onQueryFormLoaded(json) {
|
||||
console.log('onQueryFormLoaded', json);
|
||||
await getRefPromise(superQueryButtonRef);
|
||||
superQueryButtonRef.value.init(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* list页面打开 其他表单弹窗
|
||||
* @param params
|
||||
*/
|
||||
function openOnlinePopModal(params) {
|
||||
console.log('openOnlinePopModal', params);
|
||||
popTableId.value = params.id;
|
||||
let data = {
|
||||
title: params.describe,
|
||||
};
|
||||
if (params.record && params.record.id) {
|
||||
data['record'] = params.record;
|
||||
data['isUpdate'] = true;
|
||||
}
|
||||
openPopModal(true, data);
|
||||
}
|
||||
//绑定弹窗事件
|
||||
useOnlineListPopEvent(openOnlinePopModal);
|
||||
|
||||
const getSubColunm = (data) => {
|
||||
handleFormConfig(data);
|
||||
const { schema } = data;
|
||||
const { properties } = schema;
|
||||
const subMenu = [];
|
||||
Object.entries(properties).forEach(([key, value]) => {
|
||||
if (value.view == 'tab') {
|
||||
subMenu.push({ tableName: key, tableTxt: value.describe, id: value.id, order: value.order });
|
||||
}
|
||||
});
|
||||
subMenu.sort((a, b) => a.order - b.order);
|
||||
innerSubTable.tabNav = subMenu;
|
||||
};
|
||||
|
||||
const handleExpand = (expanded, record) => {
|
||||
expandedRowKeys.value = [];
|
||||
if (expanded) {
|
||||
expandedRowKeys.value = [record.id];
|
||||
mainTableSelectedRowRcord.value = record;
|
||||
}
|
||||
};
|
||||
const hanldeSuccess = (e) => {
|
||||
reload(e);
|
||||
reloadCurSubTable();
|
||||
}
|
||||
/**
|
||||
* liaozhiyang
|
||||
* 2023-12-26
|
||||
* 刷新子表
|
||||
*/
|
||||
const reloadCurSubTable = () => {
|
||||
if (expandedRowKeys.value.length) {
|
||||
const tabIndex = innerSubTable.tabIndex;
|
||||
innerSubTable.tabIndex = '-1';
|
||||
nextTick(() => {
|
||||
innerSubTable.tabIndex = tabIndex;
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
/** [表格主题样式一] 表格强制列不换行 */
|
||||
.j-table-force-nowrap {
|
||||
td,
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ant-table-selection-column {
|
||||
padding: 12px 22px !important;
|
||||
}
|
||||
|
||||
/** 列自适应,弊端会导致列宽失效 */
|
||||
&.ant-table-wrapper .ant-table-content {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
.online-cell-image {
|
||||
height: 25px !important;
|
||||
margin: 0 auto;
|
||||
max-width: 80px;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,395 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
|
||||
<!-- 加载中骨架屏 -->
|
||||
<a-skeleton v-if="tableReloading" active />
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<online-query-form
|
||||
v-show="!tableReloading"
|
||||
ref="onlineQueryFormOuter"
|
||||
:id="ID"
|
||||
:queryBtnCfg="getQueryButtonCfg"
|
||||
:resetBtnCfg="getResetButtonCfg"
|
||||
@search="queryWithCondition"
|
||||
@loaded="onQueryFormLoaded"
|
||||
/>
|
||||
|
||||
<!-- 列表 -->
|
||||
<BasicTable
|
||||
v-if="!tableReloading"
|
||||
ref="onlineTable"
|
||||
rowKey="jeecg_row_key"
|
||||
:canResize="true"
|
||||
:bordered="true"
|
||||
:showIndexColumn="false"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="pagination"
|
||||
:rowSelection="rowSelection"
|
||||
:actionColumn="actionColumn"
|
||||
:showTableSetting="true"
|
||||
:clickToRowSelect="false"
|
||||
:scroll="tableScroll"
|
||||
@table-redo="reload"
|
||||
:class="{ 'j-table-force-nowrap': enableScrollBar }"
|
||||
@change="handleChangeInTable"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
v-if="buttonSwitch.add && cgBIBtnMap['add'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['add'].buttonIcon"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<span>{{cgBIBtnMap['add'].buttonName}}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="buttonSwitch.import && cgBIBtnMap['import'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['import'].buttonIcon"
|
||||
@click="onImportExcel"
|
||||
>
|
||||
<span>{{cgBIBtnMap['import'].buttonName}}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="buttonSwitch.export && cgBIBtnMap['export'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['export'].buttonIcon"
|
||||
:loading="exportLoading"
|
||||
@click="onExportExcelOverride"
|
||||
>
|
||||
<span>{{cgBIBtnMap['export'].buttonName}}</span>
|
||||
</a-button>
|
||||
|
||||
<!-- 自定义按钮 -->
|
||||
<template v-if="cgTopButtonList && cgTopButtonList.length > 0" v-for="(item, index) in cgTopButtonList">
|
||||
<a-button
|
||||
v-if="item.optType == 'js'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonJsHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-else-if="item.optType == 'action'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonActionHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<a-button
|
||||
v-show="selectedKeys.length > 0"
|
||||
v-if="buttonSwitch.batch_delete && cgBIBtnMap['batch_delete'].enabled"
|
||||
:preIcon="cgBIBtnMap['batch_delete'].buttonIcon"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
<span>{{cgBIBtnMap['batch_delete'].buttonName}}</span>
|
||||
</a-button>
|
||||
|
||||
<online-super-query
|
||||
v-if="buttonSwitch.super_query && cgBIBtnMap['super_query'].enabled"
|
||||
ref="superQueryButtonRef"
|
||||
online
|
||||
:status="superQueryStatus"
|
||||
:queryBtnCfg="cgBIBtnMap['super_query']"
|
||||
@search="handleSuperQuery"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #fileSlot="{ text, record, column }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
|
||||
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download" size="small" @click="downloadRowFile(text, record, column, ID)">
|
||||
下载
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #imgSlot="{ text }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
|
||||
<img v-else :src="getImgView(text)" alt="图片不存在" class="online-cell-image" @click="viewOnlineCellImage(text)" />
|
||||
</template>
|
||||
|
||||
<template #htmlSlot="{ text, column, record }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
<template v-if="column.fieldHref">
|
||||
<a v-html="text" @click="handleClickFieldHref(column.fieldHref, record)"></a>
|
||||
</template>
|
||||
<div v-else v-html="text"></div>
|
||||
<!-- update-end--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
</template>
|
||||
|
||||
<template #pcaSlot="{ text, column }">
|
||||
<div :title="getPcaText(text, column)">{{ getPcaText(text, column) }}</div>
|
||||
</template>
|
||||
|
||||
<template #dateSlot="{ text, column }">
|
||||
<span>{{ getFormatDate(text, column) }}</span>
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 表单新增、修改弹框 -->
|
||||
<OnlineAutoModal
|
||||
@register="registerModal"
|
||||
:id="ID"
|
||||
:cgBIBtnMap="cgBIBtnMap"
|
||||
:buttonSwitch="buttonSwitch"
|
||||
:confirmBtnCfg="getFormConfirmButtonCfg"
|
||||
@success="reload"
|
||||
@formConfig="handleFormConfig"
|
||||
/>
|
||||
|
||||
<!-- 详情弹框 -->
|
||||
<online-detail-modal :id="ID" @register="registerDetailModal" />
|
||||
|
||||
<!-- 导入 -->
|
||||
<JImportModal @register="registerImportModal" :url="importUrl()" @ok="reload" online></JImportModal>
|
||||
|
||||
<!-- 跳转Href的动态组件方式 -->
|
||||
<a-modal v-bind="hrefComponent.model" v-on="hrefComponent.on">
|
||||
<component :is="hrefComponent.is" v-bind="hrefComponent.params" />
|
||||
</a-modal>
|
||||
|
||||
<!-- 自定义弹窗 -->
|
||||
<online-custom-modal @register="registerCustomModal" @success="reload" />
|
||||
|
||||
<!-- 弹窗给href到另外一张表单用-详情表单 -->
|
||||
<online-detail-modal :id="hrefMainTableId" @register="registerOnlineHrefModal" :defaultFullscreen="false" />
|
||||
|
||||
<!-- 弹窗到另外一张表单用-可编辑表单-关联记录的字段可在列表上打开modal编辑数据 -->
|
||||
<online-pop-modal ref="onlinePopModalRef" :id="popTableId" @register="registerPopModal" @success="reload" request topTip></online-pop-modal>
|
||||
|
||||
<!-- 流程图查看modal -->
|
||||
<BpmGraphicModal @register="registerBpmModal"></BpmGraphicModal>
|
||||
<!-- 页面loading[主要为了js增强使用] -->
|
||||
<Loading :loading="pageLoading" :absolute="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="OnlCgformTabList">
|
||||
import { ref, watch } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import OnlineCustomModal from '../default/OnlineCustomModal.vue';
|
||||
import OnlineAutoModal from './modal/OnlineTabAutoModal.vue';
|
||||
import OnlineDetailModal from './modal/OnlineTabDetailModal.vue';
|
||||
import JImportModal from '/@/components/Form/src/jeecg/components/JImportModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useTableColumns } from '../../hooks/auto/useTableColumns';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import OnlineQueryForm from '../comp/OnlineQueryForm.vue';
|
||||
import OnlineSuperQuery from '../comp/superquery/SuperQuery.vue';
|
||||
import { useOnlineListPopEvent } from '../../hooks/auto/useOnlinePopEvent';
|
||||
import OnlinePopModal from '../comp/OnlinePopModal.vue';
|
||||
import { TAB } from '../../util/constant';
|
||||
import { Loading } from '/@/components/Loading';
|
||||
|
||||
const { createMessage: $message } = useMessage();
|
||||
// 这行代码应该在每次进入新的路由都会走,不管该路由有没有被缓存--
|
||||
const {
|
||||
ID,
|
||||
onlineTableContext,
|
||||
onlineQueryFormOuter,
|
||||
loading,
|
||||
reload,
|
||||
dataSource,
|
||||
pagination,
|
||||
handleSpecialConfig,
|
||||
getColumnList,
|
||||
handleChangeInTable,
|
||||
loadData,
|
||||
superQueryButtonRef,
|
||||
superQueryStatus,
|
||||
handleSuperQuery,
|
||||
onlineExtConfigJson,
|
||||
handleFormConfig,
|
||||
registerCustomModal,
|
||||
tableReloading,
|
||||
pageLoading,
|
||||
} = useOnlineTableContext();
|
||||
|
||||
// 判断 若ID不存在就终止后续逻辑
|
||||
if (!ID.value) {
|
||||
$message.warning('地址错误, 配置ID不存在!');
|
||||
// update-begin--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
throw new Error('地址错误, 配置ID不存在!');
|
||||
// update-end--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
}
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
// 处理列表button
|
||||
const {
|
||||
buttonSwitch,
|
||||
cgBIBtnMap,
|
||||
getQueryButtonCfg,
|
||||
getResetButtonCfg,
|
||||
getFormConfirmButtonCfg,
|
||||
cgTopButtonList,
|
||||
importUrl,
|
||||
registerModal,
|
||||
handleAdd,
|
||||
handleBatchDelete,
|
||||
registerImportModal,
|
||||
onImportExcel,
|
||||
onExportExcel,
|
||||
cgButtonJsHandler,
|
||||
cgButtonActionHandler,
|
||||
getDropDownActions,
|
||||
getActions,
|
||||
initButtonList,
|
||||
initButtonSwitch,
|
||||
registerDetailModal,
|
||||
registerBpmModal,
|
||||
} = useListButton(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
const exportLoading = ref(false);
|
||||
|
||||
// 重写导出方法,防止频繁点击
|
||||
async function onExportExcelOverride() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
await onExportExcel();
|
||||
} finally {
|
||||
// 防止频繁点击,延迟1.5s关闭loading
|
||||
setTimeout(() => (exportLoading.value = false), 1500);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 BasicTable 的配置
|
||||
const {
|
||||
columns,
|
||||
actionColumn,
|
||||
selectedKeys,
|
||||
rowSelection,
|
||||
enableScrollBar,
|
||||
tableScroll,
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
handleColumnResult,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
hrefMainTableId,
|
||||
registerOnlineHrefModal,
|
||||
registerPopModal,
|
||||
openPopModal,
|
||||
onlinePopModalRef,
|
||||
popTableId,
|
||||
handleClickFieldHref,
|
||||
} = useTableColumns(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
// 监听表单配置ID
|
||||
watch(
|
||||
ID,
|
||||
() => {
|
||||
console.log('watched id is change...');
|
||||
initAutoList();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**重新加载online配置*/
|
||||
async function initAutoList() {
|
||||
loading.value = true;
|
||||
// 1.列配置信息
|
||||
let columnResult = await getColumnList(TAB);
|
||||
handleTableConfig(columnResult);
|
||||
// 2.加载数据
|
||||
await loadData();
|
||||
loading.value = false;
|
||||
// 3.执行js增强 setup
|
||||
onlineTableContext.execButtonEnhance('setup');
|
||||
}
|
||||
|
||||
// 将查询结果转成 table渲染需要的配置
|
||||
function handleTableConfig(result) {
|
||||
// js增强初始化
|
||||
let EnhanceJS = initCgEnhanceJs(result.enhanceJs);
|
||||
onlineTableContext['EnhanceJS'] = EnhanceJS;
|
||||
// 自定义按钮设置
|
||||
initButtonList(result.cgButtonList);
|
||||
// 页面按钮显示隐藏状态设置
|
||||
initButtonSwitch(result.hideColumns);
|
||||
// 列配置
|
||||
handleColumnResult(result);
|
||||
// 表配置
|
||||
handleSpecialConfig(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询控件 事件-执行查询
|
||||
* @param data
|
||||
*/
|
||||
function queryWithCondition(data) {
|
||||
onlineTableContext['queryParam'] = data;
|
||||
reload({mode:'search'});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询组件加载完成事件,获取高级查询需要的字段信息
|
||||
*/
|
||||
async function onQueryFormLoaded(json) {
|
||||
console.log('onQueryFormLoaded', json);
|
||||
await getRefPromise(superQueryButtonRef);
|
||||
superQueryButtonRef.value.init(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* list页面打开 其他表单弹窗
|
||||
* @param params
|
||||
*/
|
||||
function openOnlinePopModal(params) {
|
||||
console.log('openOnlinePopModal', params);
|
||||
popTableId.value = params.id;
|
||||
let data = {
|
||||
title: params.describe,
|
||||
};
|
||||
if (params.record && params.record.id) {
|
||||
data['record'] = params.record;
|
||||
data['isUpdate'] = true;
|
||||
}
|
||||
openPopModal(true, data);
|
||||
}
|
||||
//绑定弹窗事件
|
||||
useOnlineListPopEvent(openOnlinePopModal);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
/** [表格主题样式一] 表格强制列不换行 */
|
||||
.j-table-force-nowrap {
|
||||
td,
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ant-table-selection-column {
|
||||
padding: 12px 22px !important;
|
||||
}
|
||||
|
||||
/** 列自适应,弊端会导致列宽失效 */
|
||||
&.ant-table-wrapper .ant-table-content {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
.online-cell-image {
|
||||
height: 25px !important;
|
||||
margin: 0 auto;
|
||||
max-width: 80px;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,303 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
:title="title"
|
||||
@cancel="
|
||||
() => {
|
||||
onCloseEvent();
|
||||
restTabIndex();
|
||||
}
|
||||
"
|
||||
:enableComment="enableComment"
|
||||
:width="modalWidth"
|
||||
v-bind="$attrs"
|
||||
:maxHeight="600"
|
||||
@register="registerModal"
|
||||
wrapClassName="jeecg-online-modal"
|
||||
@ok="handleSubmit"
|
||||
@commentOpen="handleCommentOpen"
|
||||
>
|
||||
<!--update-begin--author:liaozhiyang---date:20230821---for:【QQYUN-6305】tab主题一对多-->
|
||||
<template v-if="themeTemplate === TAB" #title>
|
||||
<div class="titleArea">
|
||||
<div class="title">{{ title }}</div>
|
||||
<div class="right">
|
||||
<a-dropdown-button v-if="showDropdownBtn" trigger="click">
|
||||
{{ tabNav[+tabIndex + 1].tableTxt }}
|
||||
<template #overlay>
|
||||
<a-menu @click="handleMenuClick">
|
||||
<a-menu-item v-for="(item, index) in tabNav" :key="tabValue(index)">
|
||||
{{ item.tableTxt }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown-button>
|
||||
<a-radio-group v-else v-model:value="tabIndex">
|
||||
<a-radio-button v-for="(item, index) in tabNav" :value="tabValue(index)" :key="item.tableName">{{ item.tableTxt }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!--update-end--author:liaozhiyang---date:20230821---for:【QQYUN-6305】tab主题一对多-->
|
||||
<template #footer>
|
||||
<a-row>
|
||||
<a-col :span='24 - commentSpan'>
|
||||
<a-button
|
||||
v-for="btn in cgButtonList"
|
||||
:key="btn.id"
|
||||
type="primary"
|
||||
@click="handleCgButtonClick(btn.optType, btn.buttonCode)"
|
||||
:preIcon="btn.buttonIcon ? 'ant-design:' + btn.buttonIcon : ''"
|
||||
>
|
||||
{{ btn.buttonName }}
|
||||
</a-button>
|
||||
|
||||
<a-button
|
||||
v-if="!disableSubmit && confirmBtnCfg.enabled"
|
||||
key="submit"
|
||||
type="primary"
|
||||
:preIcon="confirmBtnCfg.buttonIcon"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
<span>{{ confirmBtnCfg.buttonName }}</span>
|
||||
</a-button>
|
||||
<a-button
|
||||
key="back"
|
||||
@click="
|
||||
() => {
|
||||
handleCancel();
|
||||
restTabIndex();
|
||||
}
|
||||
"
|
||||
>关闭</a-button
|
||||
>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<online-form
|
||||
ref="onlineFormCompRef"
|
||||
:id="id"
|
||||
:disabled="disableSubmit"
|
||||
:form-template="formTemplate"
|
||||
:isTree="isTreeForm"
|
||||
:pidField="pidFieldName"
|
||||
:themeTemplate="themeTemplate"
|
||||
:tabIndex="tabIndex"
|
||||
:cgBIBtnMap="cgBIBtnMap"
|
||||
:buttonSwitch="buttonSwitch"
|
||||
@rendered="renderSuccess"
|
||||
@success="handleSuccess"
|
||||
@toggleTab="handleToggleTab"
|
||||
>
|
||||
</online-form>
|
||||
|
||||
<template #comment>
|
||||
<comment-panel ref="commentPanelRef" :tableName="tableName" :dataId="formDataId" />
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch, ref, computed } from 'vue';
|
||||
import { BasicModal } from '/@/components/Modal';
|
||||
import OnlineForm from './OnlineTabForm.vue';
|
||||
import { useAutoModal } from '../../../hooks/auto/useAutoModal';
|
||||
import CommentPanel from '/@/components/jeecg/comment/CommentPanel.vue';
|
||||
import { TAB } from '../../../util/constant';
|
||||
import { useAppInject } from '/@/hooks/web/useAppInject';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OnlineTabAutoModal',
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
cgBIBtnMap: Object,
|
||||
buttonSwitch: Object,
|
||||
confirmBtnCfg: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
enabled: true,
|
||||
buttonName: '确定',
|
||||
buttonIcon: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
BasicModal,
|
||||
OnlineForm,
|
||||
CommentPanel,
|
||||
},
|
||||
emits: ['success', 'register', 'formConfig'],
|
||||
setup(props, { emit }) {
|
||||
console.log('进入表单弹框》》》》modal');
|
||||
const commentPanelRef = ref();
|
||||
const tabNav = ref<any>([]);
|
||||
const tabIndex = ref<any>('-1');
|
||||
const commentSpan = ref(0);
|
||||
const { getIsMobile } = useAppInject();
|
||||
|
||||
function reloadComment() {
|
||||
if (commentPanelRef.value) commentPanelRef.value.reload();
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
modalWidth,
|
||||
registerModal,
|
||||
closeModal,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
disableSubmit,
|
||||
handleSubmit,
|
||||
submitLoading,
|
||||
handleCancel,
|
||||
handleFormConfig,
|
||||
onlineFormCompRef,
|
||||
formTemplate,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
renderSuccess,
|
||||
formRendered,
|
||||
tableName,
|
||||
formDataId,
|
||||
enableComment,
|
||||
onCloseEvent,
|
||||
themeTemplate,
|
||||
} = useAutoModal(false, { emit }, reloadComment);
|
||||
|
||||
function handleSuccess(formData) {
|
||||
emit('success', formData);
|
||||
closeModal();
|
||||
// 提交完成 触发关闭事件
|
||||
onCloseEvent();
|
||||
restTabIndex();
|
||||
}
|
||||
|
||||
// 监听id变化 表单重新渲染
|
||||
watch(() => props.id, renderFormItems, { immediate: true });
|
||||
async function renderFormItems() {
|
||||
formRendered.value = false;
|
||||
if (!props.id) {
|
||||
return;
|
||||
}
|
||||
console.log('重新渲染表单》》》》modal');
|
||||
await handleFormConfig(props.id, {}, (result) => {
|
||||
const nav: any = [];
|
||||
const sub: any = [];
|
||||
const { head, schema } = result;
|
||||
const { properties } = schema;
|
||||
nav.push({ tableName: head.tableName, tableTxt: head.tableTxt });
|
||||
Object.entries(properties).forEach(([key, value]: any) => {
|
||||
if (value.view == 'tab') {
|
||||
sub.push({ tableName: key, tableTxt: value.describe, order: value.order });
|
||||
}
|
||||
});
|
||||
sub.sort((a, b) => a.order - b.order);
|
||||
tabNav.value = [...nav, ...sub];
|
||||
});
|
||||
}
|
||||
const tabValue = (index) => {
|
||||
return String(index - 1);
|
||||
};
|
||||
const restTabIndex = () => {
|
||||
// 关闭的时候把索引重置第一位(防止下一条数据打开还是同样的tab)
|
||||
setTimeout(() => {
|
||||
tabIndex.value = '-1';
|
||||
}, 500);
|
||||
};
|
||||
// 移动端且大于2条数据时显示下拉菜单
|
||||
const showDropdownBtn = computed(() => {
|
||||
return getIsMobile.value && tabNav.value.length > 2;
|
||||
});
|
||||
const handleMenuClick = ({ key }) => {
|
||||
tabIndex.value = key;
|
||||
};
|
||||
const handleToggleTab = (key) => {
|
||||
tabIndex.value = key;
|
||||
};
|
||||
// update-begin--author:liaozhiyang---date:20240528---for:【TV360X-485】开启评论之后弹窗按钮居右隔一个评论的距离
|
||||
const handleCommentOpen = (visible, span) => {
|
||||
console.log('评论是否展开:', visible);
|
||||
commentSpan.value = span;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240528---for:【TV360X-485】开启评论之后弹窗按钮居右隔一个评论的距离
|
||||
const that = {
|
||||
title,
|
||||
onlineFormCompRef,
|
||||
renderSuccess,
|
||||
registerModal,
|
||||
handleSubmit,
|
||||
handleSuccess,
|
||||
handleCancel,
|
||||
modalWidth,
|
||||
formTemplate,
|
||||
disableSubmit,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
submitLoading,
|
||||
tableName,
|
||||
formDataId,
|
||||
enableComment,
|
||||
commentPanelRef,
|
||||
onCloseEvent,
|
||||
themeTemplate,
|
||||
tabNav,
|
||||
tabValue,
|
||||
tabIndex,
|
||||
TAB,
|
||||
restTabIndex,
|
||||
handleMenuClick,
|
||||
showDropdownBtn,
|
||||
handleToggleTab,
|
||||
handleCommentOpen,
|
||||
commentSpan,
|
||||
};
|
||||
|
||||
return that;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.titleArea {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
padding-right: 70px;
|
||||
.title {
|
||||
margin-right: 16px;
|
||||
line-height: 32px;
|
||||
}
|
||||
.right {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
.ant-radio-group {
|
||||
font-weight: normal;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.footerWrap {
|
||||
&.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
html[data-theme='light'] {
|
||||
.right {
|
||||
.ant-radio-group {
|
||||
:deep(.ant-radio-button-wrapper:not(.ant-radio-button-wrapper-checked)) {
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
:title="title"
|
||||
@cancel="
|
||||
() => {
|
||||
restTabIndex();
|
||||
}
|
||||
"
|
||||
:width="modalWidth"
|
||||
:maxHeight="600"
|
||||
:enableComment="enableComment"
|
||||
:defaultFullscreen="false"
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
wrapClassName="jeecg-online-detail-modal"
|
||||
>
|
||||
<template #title>
|
||||
<div class="titleArea">
|
||||
<div class="title">{{ title }}</div>
|
||||
<div class="right">
|
||||
<a-radio-group v-model:value="tabIndex">
|
||||
<a-radio-button v-for="(item, index) in tabNav" :value="tabValue(index)" :key="item.tableName">{{ item.tableTxt }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<a-button
|
||||
key="back"
|
||||
@click="
|
||||
() => {
|
||||
handleCancel();
|
||||
restTabIndex();
|
||||
}
|
||||
"
|
||||
>关闭</a-button
|
||||
>
|
||||
</template>
|
||||
<online-form-detail
|
||||
ref="onlineFormCompRef"
|
||||
:id="id"
|
||||
:form-template="formTemplate"
|
||||
:show-sub="showSub"
|
||||
:themeTemplate="themeTemplate"
|
||||
:tabIndex="tabIndex"
|
||||
@rendered="renderSuccess"
|
||||
/>
|
||||
|
||||
<template #comment>
|
||||
<comment-panel ref="commentPanelRef" :tableName="tableName" :dataId="formDataId"></comment-panel>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch, ref } from 'vue';
|
||||
import { BasicModal } from '/@/components/Modal';
|
||||
import OnlineFormDetail from './OnlineTabFormDetail.vue';
|
||||
import { useAutoModal } from '../../../hooks/auto/useAutoModal';
|
||||
import CommentPanel from '/@/components/jeecg/comment/CommentPanel.vue';
|
||||
import { TAB } from '../../../util/constant';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OnlineTabDetailModal',
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
BasicModal,
|
||||
OnlineFormDetail,
|
||||
CommentPanel,
|
||||
},
|
||||
emits: ['success', 'register', 'formConfig'],
|
||||
setup(props, { emit }) {
|
||||
console.log('进入表单弹框》》》》modal');
|
||||
|
||||
const commentPanelRef = ref();
|
||||
const tabNav = ref<any>([]);
|
||||
const tabIndex = ref<string>('-1');
|
||||
function reloadComment() {
|
||||
if (commentPanelRef.value) commentPanelRef.value.reload();
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
modalWidth,
|
||||
registerModal,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
disableSubmit,
|
||||
handleSubmit,
|
||||
submitLoading,
|
||||
handleCancel,
|
||||
handleFormConfig,
|
||||
onlineFormCompRef,
|
||||
formTemplate,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
renderSuccess,
|
||||
formRendered,
|
||||
showSub,
|
||||
tableName,
|
||||
formDataId,
|
||||
enableComment,
|
||||
themeTemplate,
|
||||
} = useAutoModal(false, { emit }, reloadComment);
|
||||
|
||||
// 监听id变化 表单重新渲染
|
||||
watch(() => props.id, renderFormItems, { immediate: true });
|
||||
async function renderFormItems() {
|
||||
formRendered.value = false;
|
||||
if (!props.id) {
|
||||
return;
|
||||
}
|
||||
console.log('重新渲染表单》》》》modal');
|
||||
await handleFormConfig(props.id, {}, (result) => {
|
||||
const nav: any = [];
|
||||
const sub: any = [];
|
||||
const { head, schema } = result;
|
||||
const { properties } = schema;
|
||||
nav.push({ tableName: head.tableName, tableTxt: head.tableTxt });
|
||||
Object.entries(properties).forEach(([key, value]: any) => {
|
||||
if (value.view == 'tab') {
|
||||
//update-begin---author:chenrui ---date:2025/8/27 for:[issues/8760]online表单中,主题模板为“TAB主题”时,附表TAB页面的标题和内容不一致 #8760------------
|
||||
sub.push({ tableName: key, tableTxt: value.describe,order: value.order });
|
||||
//update-end---author:chenrui ---date:2025/8/27 for:[issues/8760]online表单中,主题模板为“TAB主题”时,附表TAB页面的标题和内容不一致 #8760------------
|
||||
}
|
||||
});
|
||||
sub.sort((a, b) => a.order - b.order);
|
||||
tabNav.value = [...nav, ...sub];
|
||||
});
|
||||
}
|
||||
const tabValue = (index) => {
|
||||
return String(index - 1);
|
||||
};
|
||||
const restTabIndex = () => {
|
||||
// 关闭的时候把索引重置第一位(防止下一条数据打开还是同样的tab)
|
||||
setTimeout(() => {
|
||||
tabIndex.value = '-1';
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const that = {
|
||||
title,
|
||||
onlineFormCompRef,
|
||||
renderSuccess,
|
||||
registerModal,
|
||||
handleSubmit,
|
||||
handleCancel,
|
||||
modalWidth,
|
||||
formTemplate,
|
||||
disableSubmit,
|
||||
cgButtonList,
|
||||
handleCgButtonClick,
|
||||
isTreeForm,
|
||||
pidFieldName,
|
||||
submitLoading,
|
||||
showSub,
|
||||
tableName,
|
||||
formDataId,
|
||||
enableComment,
|
||||
commentPanelRef,
|
||||
themeTemplate,
|
||||
tabNav,
|
||||
tabValue,
|
||||
tabIndex,
|
||||
TAB,
|
||||
restTabIndex,
|
||||
};
|
||||
|
||||
return that;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.titleArea {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
padding-right: 70px;
|
||||
.title {
|
||||
margin-right: 16px;
|
||||
line-height: 32px;
|
||||
}
|
||||
.right {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,343 @@
|
||||
<template>
|
||||
<div :id="tableName + '_form'">
|
||||
<!-- 积木报表的打印按钮,只有配置了 reportUrl 才显示 -->
|
||||
<div
|
||||
v-if="!!formData.id && !!onlineExtConfigJson.reportPrintShow"
|
||||
style="text-align: right; position: absolute; top: 15px; right: 20px; z-index: 999"
|
||||
>
|
||||
<PrinterOutlined title="打印" @click="onOpenReportPrint" style="font-size: 16px" />
|
||||
</div>
|
||||
|
||||
<a-tabs
|
||||
class="tabTheme"
|
||||
@change="onTabChange"
|
||||
v-model:activeKey="subActiveKey"
|
||||
>
|
||||
<a-tab-pane tab="主表" :key="'-1'">
|
||||
<detail-form :schemas="detailFormSchemas" :data="formData" :span="formSpan" />
|
||||
</a-tab-pane>
|
||||
<template v-if="hasSubTable && showSub">
|
||||
<a-tab-pane v-for="(sub, index) in subTabInfo" :tab="sub.describe" :key="index + ''" :forceRender="true">
|
||||
<div :style="{ 'overflow-y': 'auto', 'overflow-x': 'hidden', 'max-height': subFormHeight + 'px' }" v-if="sub.relationType == 1">
|
||||
<!-- 子表-一对一 -->
|
||||
<online-sub-form-detail
|
||||
:key="subReloadKey"
|
||||
:table="sub.key"
|
||||
:form-template="formTemplate"
|
||||
:main-id="getSubTableForeignKeyValue(sub.foreignKey)"
|
||||
:properties="sub.properties"
|
||||
>
|
||||
</online-sub-form-detail>
|
||||
</div>
|
||||
<div v-else>
|
||||
<!-- 子表-一对多 -->
|
||||
<JVxeTable
|
||||
v-if="showStatus[sub.key]"
|
||||
:ref="refMap[sub.key]"
|
||||
toolbar
|
||||
keep-source
|
||||
:row-number="rowNumber"
|
||||
row-selection
|
||||
:height="subTableHeight"
|
||||
:disabled="true"
|
||||
:columns="sub.columns"
|
||||
:dataSource="subDataSource[sub.key]"
|
||||
:authPre="getSubTableAuthPre(sub.key)"
|
||||
/>
|
||||
<a-spin v-else :spinning="true" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</template>
|
||||
</a-tabs>
|
||||
<Loading :loading="loading" :absolute="false" />
|
||||
<slot name="bottom"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Loading } from '/@/components/Loading';
|
||||
import { getToken } from '/@/utils/auth';
|
||||
import { goJmReportViewPage } from '/@/utils';
|
||||
import { PrinterOutlined } from '@ant-design/icons-vue';
|
||||
import DetailForm from '../../../extend/form/DetailForm.vue';
|
||||
import OnlineSubFormDetail from '../../comp/OnlineSubFormDetail.vue';
|
||||
import { getDetailFormSchemas } from '../../../hooks/auto/useAutoForm';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { ERP, TAB } from '../../../util/constant';
|
||||
import { useAppInject } from '/@/hooks/web/useAppInject';
|
||||
|
||||
export default {
|
||||
name: 'OnlineTabFormDetail',
|
||||
components: {
|
||||
DetailForm,
|
||||
Loading,
|
||||
PrinterOutlined,
|
||||
OnlineSubFormDetail,
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
formTemplate: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isTree: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
pidField: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
submitTip: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showSub: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
themeTemplate: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
tabIndex: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
emits: ['success', 'rendered'],
|
||||
setup(props, { emit }) {
|
||||
console.log('onlineForm-setup》》');
|
||||
const { createMessage: $message } = useMessage();
|
||||
const { getIsMobile } = useAppInject();
|
||||
|
||||
const tableName = ref('');
|
||||
const single = ref(true);
|
||||
// 加载状态
|
||||
const loading = ref(false);
|
||||
const tableType = ref(1);
|
||||
const formData = ref<any>({});
|
||||
// update-begin-author:liaozhiyang---date:20240313---for:【QQYUN-9034】online弹窗一对一子表移动端内容高度设置不合理
|
||||
const subFormHeight = ref('auto');
|
||||
// update-end-author:liaozhiyang---date:20240313---for:【QQYUN-9034】online弹窗一对一子表移动端内容高度设置不合理
|
||||
const subReloadKey = ref(0);
|
||||
// 子表表格高度
|
||||
// 【VUEN-803】一对多子表固定340高度,修复自定义列组件被遮挡的问题
|
||||
const subTableHeight = ref(340);
|
||||
const subActiveKey = ref('0');
|
||||
|
||||
const rowNumber = ref(getIsMobile.value ? false : true);
|
||||
|
||||
watch(
|
||||
() => props.tabIndex,
|
||||
(value, oldValue) => {
|
||||
subActiveKey.value = value;
|
||||
if (oldValue) {
|
||||
//第一次进来不执行,之后监听变化了才执行。(详情情界面序号错位了,点一下正常)
|
||||
onTabChange();
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
/**
|
||||
* online表单扩展配置
|
||||
*/
|
||||
const onlineExtConfigJson = reactive({
|
||||
reportPrintShow: 0,
|
||||
reportPrintUrl: '',
|
||||
joinQuery: 0,
|
||||
modelFullscreen: 0,
|
||||
modalMinWidth: '',
|
||||
});
|
||||
|
||||
const { detailFormSchemas, hasSubTable, subTabInfo, refMap, showStatus, subDataSource, createFormSchemas, formSpan } =
|
||||
getDetailFormSchemas(props);
|
||||
|
||||
/**
|
||||
* 处理扩展配置
|
||||
*/
|
||||
function handleExtConfigJson(jsonStr) {
|
||||
let extConfigJson = { reportPrintShow: 0, reportPrintUrl: '', joinQuery: 0, modelFullscreen: 1, modalMinWidth: '' };
|
||||
if (jsonStr) {
|
||||
extConfigJson = JSON.parse(jsonStr);
|
||||
}
|
||||
Object.keys(extConfigJson).map((k) => {
|
||||
onlineExtConfigJson[k] = extConfigJson[k];
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染表单
|
||||
async function createRootProperties(data) {
|
||||
tableType.value = data.head.tableType;
|
||||
tableName.value = data.head.tableName;
|
||||
single.value = data.head.tableType == 1;
|
||||
handleExtConfigJson(data.head.extConfigJson);
|
||||
createFormSchemas(data.schema.properties);
|
||||
emit('rendered', onlineExtConfigJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* status: 是否是修改页面
|
||||
* record: 列表页面的行数据
|
||||
* param: 树形列表添加子节点 传入的父级节点id
|
||||
* */
|
||||
async function show(_status, record) {
|
||||
console.log('进入表单详情》》form', record);
|
||||
// -update-begin--author:liaozhiyang---date:20251209---for:【QQYUN-13970】一对一子表编辑之后查看详情不会更新
|
||||
subReloadKey.value++;
|
||||
// -update-end--author:liaozhiyang---date:20251209---for:【QQYUN-13970】一对一子表编辑之后查看详情不会更新
|
||||
await edit(record);
|
||||
changeShowStatus(true);
|
||||
}
|
||||
|
||||
function getFormData(dataId) {
|
||||
let url = `/online/cgform/api/detail/${props.id}/${dataId}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
defHttp
|
||||
.get({ url }, { isTransformResponse: false })
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
resolve(res.result);
|
||||
} else {
|
||||
reject();
|
||||
$message.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//update-begin-author:taoyan date:2023-2-13 for: QQYUN-4226【vue3】online 一对多子表 详情界面,序号错位了 点一下子表表格就正常了
|
||||
function changeShowStatus(flag) {
|
||||
Object.keys(showStatus).map((k) => {
|
||||
showStatus[k] = flag;
|
||||
});
|
||||
}
|
||||
|
||||
function onTabChange() {
|
||||
changeShowStatus(false);
|
||||
setTimeout(() => {
|
||||
changeShowStatus(true);
|
||||
}, 300);
|
||||
}
|
||||
//update-end-author:taoyan date:2023-2-13 for: QQYUN-4226【vue3】online 一对多子表 详情界面,序号错位了 点一下子表表格就正常了
|
||||
|
||||
async function edit(record) {
|
||||
let temp: any = await getFormData(record.id);
|
||||
//表单赋值
|
||||
formData.value = { ...temp };
|
||||
editSubVxeTableData(temp);
|
||||
}
|
||||
|
||||
function editSubVxeTableData(record) {
|
||||
if (!record) {
|
||||
// 新增页面需要清空子表数据
|
||||
record = {};
|
||||
}
|
||||
let keys = Object.keys(subDataSource.value);
|
||||
if (keys && keys.length > 0) {
|
||||
let obj = {};
|
||||
for (let key of keys) {
|
||||
obj[key] = record[key] || [];
|
||||
}
|
||||
subDataSource.value = obj;
|
||||
}
|
||||
}
|
||||
|
||||
function getSubTableAuthPre(table) {
|
||||
return 'online_' + table + ':';
|
||||
}
|
||||
|
||||
//跳转至积木报表页面
|
||||
function onOpenReportPrint() {
|
||||
let url = onlineExtConfigJson.reportPrintUrl;
|
||||
let temp: any = formData.value;
|
||||
if (temp) {
|
||||
let id = temp.id;
|
||||
let token = getToken();
|
||||
goJmReportViewPage(url, id, token);
|
||||
}
|
||||
}
|
||||
|
||||
function getSubTableForeignKeyValue(key) {
|
||||
let temp = formData.value;
|
||||
console.log('getValueIgnoreCase(temp, key)', temp, key, getValueIgnoreCase(temp, key));
|
||||
return getValueIgnoreCase(temp, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* VUEN-1056 30、生成的一对多,编辑的时候,子表数据挂不上
|
||||
*/
|
||||
function getValueIgnoreCase(data, key) {
|
||||
if (data) {
|
||||
let temp = data[key];
|
||||
if (!temp && temp !== 0) {
|
||||
temp = data[key.toLowerCase()];
|
||||
if (!temp && temp !== 0) {
|
||||
temp = data[key.toUpperCase()];
|
||||
}
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
return {
|
||||
detailFormSchemas,
|
||||
formData,
|
||||
formSpan,
|
||||
|
||||
//主表
|
||||
tableName,
|
||||
loading,
|
||||
|
||||
//子表
|
||||
hasSubTable,
|
||||
subTabInfo,
|
||||
subFormHeight,
|
||||
subTableHeight,
|
||||
refMap,
|
||||
onTabChange,
|
||||
subReloadKey,
|
||||
//一对多子表
|
||||
subDataSource,
|
||||
getSubTableAuthPre,
|
||||
|
||||
//父组件调用
|
||||
show,
|
||||
createRootProperties,
|
||||
|
||||
// 扩展配置
|
||||
onOpenReportPrint,
|
||||
onlineExtConfigJson,
|
||||
getSubTableForeignKeyValue,
|
||||
showStatus,
|
||||
ERP,
|
||||
TAB,
|
||||
subActiveKey,
|
||||
rowNumber,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.tabTheme {
|
||||
:deep(.ant-tabs-nav) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,588 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
|
||||
<!-- 加载中骨架屏 -->
|
||||
<a-skeleton v-if="tableReloading" active />
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<online-query-form
|
||||
v-show="!tableReloading"
|
||||
ref="onlineQueryFormOuter"
|
||||
:id="ID"
|
||||
:queryBtnCfg="getQueryButtonCfg"
|
||||
:resetBtnCfg="getResetButtonCfg"
|
||||
@search="queryWithCondition"
|
||||
@loaded="onQueryFormLoaded"
|
||||
/>
|
||||
|
||||
<!-- 列表-树形列表前五个配置不一样【树】 -->
|
||||
<BasicTable
|
||||
v-if="!tableReloading"
|
||||
ref="onlineTreeTableRef"
|
||||
:isTreeTable="true"
|
||||
:expandedRowKeys="expandedRowKeys"
|
||||
@expandedRowsChange="handleExpandedRowsChange"
|
||||
@expand="handleExpand"
|
||||
rowKey="jeecg_row_key"
|
||||
:canResize="true"
|
||||
:bordered="true"
|
||||
:showIndexColumn="false"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:pagination="pagination"
|
||||
:rowSelection="rowSelection"
|
||||
:actionColumn="actionColumn"
|
||||
:showTableSetting="true"
|
||||
:clickToRowSelect="false"
|
||||
:scroll="tableScroll"
|
||||
@table-redo="reload"
|
||||
:class="{ 'j-table-force-nowrap': enableScrollBar }"
|
||||
@change="handleChangeInTable"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
v-if="buttonSwitch.add && cgBIBtnMap['add'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['add'].buttonIcon"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<span>{{cgBIBtnMap['add'].buttonName}}</span>
|
||||
</a-button>
|
||||
<!-- <a-button-->
|
||||
<!-- v-if="buttonSwitch.import && cgBIBtnMap['import'].enabled"-->
|
||||
<!-- type="primary"-->
|
||||
<!-- :preIcon="cgBIBtnMap['import'].buttonIcon"-->
|
||||
<!-- @click="onImportExcel"-->
|
||||
<!-- >-->
|
||||
<!-- <span>{{cgBIBtnMap['import'].buttonName}}</span>-->
|
||||
<!-- </a-button>-->
|
||||
<a-button
|
||||
v-if="buttonSwitch.export && cgBIBtnMap['export'].enabled"
|
||||
type="primary"
|
||||
:preIcon="cgBIBtnMap['export'].buttonIcon"
|
||||
@click="onExportExcel"
|
||||
>
|
||||
<span>{{cgBIBtnMap['export'].buttonName}}</span>
|
||||
</a-button>
|
||||
|
||||
<!-- 自定义按钮 -->
|
||||
<template v-if="cgTopButtonList && cgTopButtonList.length > 0" v-for="(item, index) in cgTopButtonList">
|
||||
<a-button
|
||||
v-if="item.optType == 'js'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonJsHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
<a-button
|
||||
v-else-if="item.optType == 'action'"
|
||||
:key="'cgbtn' + index"
|
||||
@click="cgButtonActionHandler(item.buttonCode)"
|
||||
type="primary"
|
||||
:preIcon="item.buttonIcon ? 'ant-design:' + item.buttonIcon : ''"
|
||||
>
|
||||
{{ item.buttonName }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<a-button
|
||||
v-show="selectedKeys.length > 0"
|
||||
v-if="buttonSwitch.batch_delete && cgBIBtnMap['batch_delete'].enabled"
|
||||
:preIcon="cgBIBtnMap['batch_delete'].buttonIcon"
|
||||
@click="handleBatchDelete"
|
||||
>
|
||||
<span>{{cgBIBtnMap['batch_delete'].buttonName}}</span>
|
||||
</a-button>
|
||||
|
||||
<online-super-query
|
||||
v-if="buttonSwitch.super_query && cgBIBtnMap['super_query'].enabled"
|
||||
ref="superQueryButtonRef"
|
||||
online
|
||||
:status="superQueryStatus"
|
||||
:queryBtnCfg="cgBIBtnMap['super_query']"
|
||||
@search="handleSuperQuery"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #fileSlot="{ text, record, column }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
|
||||
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download" size="small" @click="downloadRowFile(text, record, column, ID)">
|
||||
下载
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<template #imgSlot="{ text }">
|
||||
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
|
||||
<img v-else :src="getImgView(text)" alt="图片不存在" class="online-cell-image" @click="viewOnlineCellImage(text)" />
|
||||
</template>
|
||||
|
||||
<template #htmlSlot="{ text, column, record }">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
<template v-if="column.fieldHref">
|
||||
<a v-html="text" @click="handleClickFieldHref(column.fieldHref, record)"></a>
|
||||
</template>
|
||||
<div v-else v-html="text"></div>
|
||||
<!-- update-end--author:liaozhiyang---date:20240517---for:【TV360X-129】增加富文本控件配置href跳转 -->
|
||||
</template>
|
||||
|
||||
<template #pcaSlot="{ text, column }">
|
||||
<div :title="getPcaText(text, column)">{{ getPcaText(text, column) }}</div>
|
||||
</template>
|
||||
|
||||
<template #dateSlot="{ text, column }">
|
||||
<span>{{ getFormatDate(text, column) }}</span>
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getActions(record)" :dropDownActions="getTreeDropDownActions(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 表单新增、修改弹框 -->
|
||||
<OnlineAutoModal
|
||||
@register="registerModal"
|
||||
:id="ID"
|
||||
:cgBIBtnMap="cgBIBtnMap"
|
||||
:buttonSwitch="buttonSwitch"
|
||||
:confirmBtnCfg="getFormConfirmButtonCfg"
|
||||
@success="handlerFormSuccess"
|
||||
@formConfig="handleFormConfig"
|
||||
/>
|
||||
|
||||
<!-- 导入 -->
|
||||
<JImportModal @register="registerImportModal" :url="importUrl()" @ok="reload" online></JImportModal>
|
||||
|
||||
<!-- 跳转Href的动态组件方式 -->
|
||||
<a-modal v-bind="hrefComponent.model" v-on="hrefComponent.on">
|
||||
<component :is="hrefComponent.is" v-bind="hrefComponent.params" />
|
||||
</a-modal>
|
||||
|
||||
<!-- 自定义弹窗 -->
|
||||
<online-custom-modal @register="registerCustomModal" @success="reload" />
|
||||
|
||||
<!-- 详情弹框 -->
|
||||
<online-detail-modal :id="ID" @register="registerDetailModal"/>
|
||||
|
||||
<!-- 流程图查看modal -->
|
||||
<BpmGraphicModal @register="registerBpmModal"></BpmGraphicModal>
|
||||
<!-- 页面loading[主要为了js增强使用] -->
|
||||
<Loading :loading="pageLoading" :absolute="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import OnlineAutoModal from '../default/OnlineAutoModal.vue';
|
||||
import OnlineDetailModal from '../default/OnlineDetailModal.vue';
|
||||
import OnlineCustomModal from '../default/OnlineCustomModal.vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import JImportModal from '/@/components/Form/src/jeecg/components/JImportModal.vue';
|
||||
import { useOnlineTableContext } from '../../hooks/auto/useOnlineTableContext';
|
||||
import { useListButton } from '../../hooks/auto/useListButton';
|
||||
import { useTableColumns } from '../../hooks/auto/useTableColumns';
|
||||
import { useEnhance } from '../../hooks/auto/useEnhance';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { getRefPromise } from '../../hooks/auto/useAutoForm';
|
||||
import OnlineQueryForm from '../comp/OnlineQueryForm.vue';
|
||||
import OnlineSuperQuery from '../comp/superquery/SuperQuery.vue';
|
||||
import { Tree } from "../../util/constant";
|
||||
import { Loading } from '/@/components/Loading';
|
||||
|
||||
export default {
|
||||
name: 'DefaultOnlineList',
|
||||
components: {
|
||||
BasicTable,
|
||||
TableAction,
|
||||
OnlineAutoModal,
|
||||
JImportModal,
|
||||
OnlineQueryForm,
|
||||
OnlineSuperQuery,
|
||||
OnlineCustomModal,
|
||||
OnlineDetailModal,
|
||||
Loading,
|
||||
},
|
||||
setup() {
|
||||
const { createMessage: $message } = useMessage();
|
||||
const onlineTreeTableRef = ref();
|
||||
|
||||
// 树形列表增加了最后3个配置项【树】
|
||||
const {
|
||||
ID,
|
||||
onlineTableContext,
|
||||
onlineQueryFormOuter,
|
||||
loading,
|
||||
reload,
|
||||
dataSource,
|
||||
pagination,
|
||||
handleSpecialConfig,
|
||||
getColumnList,
|
||||
handleChangeInTable,
|
||||
loadData,
|
||||
superQueryButtonRef,
|
||||
superQueryStatus,
|
||||
handleSuperQuery,
|
||||
registerCustomModal,
|
||||
getTreeDataByResult,
|
||||
expandedRowKeys,
|
||||
handleExpandedRowsChange,
|
||||
tableReloading,
|
||||
onlineExtConfigJson,
|
||||
handleFormConfig,
|
||||
pageLoading,
|
||||
} = useOnlineTableContext();
|
||||
|
||||
if (!ID.value) {
|
||||
$message.warning('地址错误, 配置ID不存在!');
|
||||
// update-begin--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
throw new Error('地址错误, 配置ID不存在!');
|
||||
// update-end--author:liaozhiyang---date:20230825---for:【QQYUN-6326】部分代码找不到引用
|
||||
}
|
||||
// 树列表特定方法调用
|
||||
onlineTableContext.isTree(true);
|
||||
|
||||
// 处理增强
|
||||
let { initCgEnhanceJs } = useEnhance(onlineTableContext);
|
||||
// 处理列表button
|
||||
const {
|
||||
buttonSwitch,
|
||||
cgLinkButtonList,
|
||||
cgBIBtnMap,
|
||||
getQueryButtonCfg,
|
||||
getResetButtonCfg,
|
||||
getFormConfirmButtonCfg,
|
||||
cgTopButtonList,
|
||||
importUrl,
|
||||
registerModal,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
handleBatchDelete,
|
||||
registerImportModal,
|
||||
onImportExcel,
|
||||
onExportExcel,
|
||||
cgButtonJsHandler,
|
||||
cgButtonActionHandler,
|
||||
cgButtonLinkHandler,
|
||||
handleSubmitFlow,
|
||||
getDropDownActions,
|
||||
getActions,
|
||||
initButtonList,
|
||||
initButtonSwitch,
|
||||
registerDetailModal,
|
||||
registerBpmModal,
|
||||
} = useListButton(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
// 处理table的配置
|
||||
const {
|
||||
columns,
|
||||
actionColumn,
|
||||
selectedKeys,
|
||||
rowSelection,
|
||||
enableScrollBar,
|
||||
tableScroll,
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
handleColumnResult,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
handleClickFieldHref,
|
||||
} = useTableColumns(onlineTableContext, onlineExtConfigJson);
|
||||
|
||||
watch(
|
||||
ID,
|
||||
() => {
|
||||
initAutoList();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
async function initAutoList() {
|
||||
loading.value = true;
|
||||
// 1.列配置信息
|
||||
let columnResult = await getColumnList(Tree);
|
||||
handleTableConfig(columnResult);
|
||||
// 2.加载数据
|
||||
await loadData();
|
||||
loading.value = false;
|
||||
// 3.执行js增强 setup
|
||||
onlineTableContext.execButtonEnhance('setup');
|
||||
}
|
||||
|
||||
// 将查询结果转成 table渲染需要的配置
|
||||
function handleTableConfig(result) {
|
||||
// js增强初始化
|
||||
let EnhanceJS = initCgEnhanceJs(result.enhanceJs);
|
||||
onlineTableContext['EnhanceJS'] = EnhanceJS;
|
||||
// 自定义按钮设置
|
||||
initButtonList(result.cgButtonList);
|
||||
// 页面按钮显示隐藏状态设置
|
||||
initButtonSwitch(result.hideColumns);
|
||||
// 列配置
|
||||
handleColumnResult(result);
|
||||
// 表配置
|
||||
handleSpecialConfig(result);
|
||||
//是否有子节点列-树列表特定【树】
|
||||
onlineTableContext['hasChildrenField'] = result.hasChildrenField;
|
||||
onlineTableContext['pidField'] = result.pidField;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询控件 事件-执行查询
|
||||
* @param data
|
||||
*/
|
||||
function queryWithCondition(data, status) {
|
||||
onlineTableContext['queryParam'] = data;
|
||||
if (status === true) {
|
||||
// 正常查询
|
||||
reload({mode:'search'});
|
||||
} else {
|
||||
//重置查询【树】
|
||||
searchReset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询组件加载完成事件,获取高级查询需要的字段信息
|
||||
*/
|
||||
async function onQueryFormLoaded(json) {
|
||||
await getRefPromise(superQueryButtonRef)
|
||||
superQueryButtonRef.value.init(json);
|
||||
}
|
||||
|
||||
/*-------------------------------以下方法是树列表专有的,或复写或新增-【树】----------------------------*/
|
||||
|
||||
// 展开节点调用
|
||||
function handleExpand(expanded, record) {
|
||||
// 判断是否是展开状态
|
||||
let expandedRowKeysValue = expandedRowKeys.value;
|
||||
if (expanded) {
|
||||
addExpandedRowKey(record.id);
|
||||
if (record.children.length > 0 && record.children[0].isLoading === true) {
|
||||
let hasChildrenField = onlineTableContext.hasChildrenField;
|
||||
const { sortField, sortType } = onlineTableContext;
|
||||
let params = Object.assign({}, { column: sortField, order: sortType });
|
||||
params[onlineTableContext['pidField']] = record.id;
|
||||
params[hasChildrenField] = record[hasChildrenField];
|
||||
let url = `${onlineTableContext.onlineUrl.getTreeData}${onlineTableContext.ID}`;
|
||||
defHttp
|
||||
.get({ url, params }, { isTransformResponse: false })
|
||||
.then((res) => {
|
||||
console.log('handleExpand', res.result);
|
||||
if (res.success) {
|
||||
if (Number(res.result.total) > 0) {
|
||||
record.children = getTreeDataByResult(res.result.records);
|
||||
// dataSource.value =
|
||||
} else {
|
||||
record.children = '';
|
||||
record.hasChildrenField = '0';
|
||||
}
|
||||
} else {
|
||||
$message.warning(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
$message.warning('加载子节点失败!');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let keyIndex = expandedRowKeysValue.indexOf(record.id);
|
||||
if (keyIndex >= 0) {
|
||||
expandedRowKeys.value = expandedRowKeysValue.splice(keyIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加展开项的id
|
||||
* */
|
||||
function addExpandedRowKey(key) {
|
||||
let arr = expandedRowKeys.value;
|
||||
if (arr && arr.indexOf(key) < 0) {
|
||||
arr.push(key);
|
||||
}
|
||||
expandedRowKeys.value = arr;
|
||||
}
|
||||
|
||||
// 重置查询条件
|
||||
async function searchReset() {
|
||||
if (onlineTableContext.isTree() === true) {
|
||||
expandedRowKeys.value = [];
|
||||
onlineTreeTableRef.value.collapseAll();
|
||||
}
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 树表单新增、修改完成后 列表页面的回调事件
|
||||
*/
|
||||
function handlerFormSuccess(formData) {
|
||||
console.log('expandedRowKeys.value', expandedRowKeys.value);
|
||||
if (loadParent.value === true) {
|
||||
let pid = formData[onlineTableContext.pidField];
|
||||
if (pid) {
|
||||
let arr = expandedRowKeys.value;
|
||||
if (arr.indexOf(pid) < 0) {
|
||||
arr.push(pid);
|
||||
}
|
||||
expandedRowKeys.value = arr;
|
||||
}
|
||||
}
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* [更多]下拉项中的 [添加子节点]
|
||||
*/
|
||||
const addChildButton = (record) => {
|
||||
return {
|
||||
label: '添加下级',
|
||||
onClick: handleAddChild.bind(null, record),
|
||||
};
|
||||
};
|
||||
|
||||
const loadParent = ref(false);
|
||||
function handleAddChild(record) {
|
||||
loadParent.value = true;
|
||||
let param = {
|
||||
[onlineTableContext.pidField]: record['id'],
|
||||
};
|
||||
handleAdd(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作列[更多下拉项]
|
||||
*/
|
||||
function getTreeDropDownActions(record) {
|
||||
let arr = getDropDownActions(record, {'themeTemplate': Tree});
|
||||
arr.unshift(addChildButton(record));
|
||||
return arr;
|
||||
}
|
||||
|
||||
const that = {
|
||||
ID,
|
||||
// 查询区域
|
||||
onlineQueryFormOuter,
|
||||
queryWithCondition,
|
||||
onQueryFormLoaded,
|
||||
reload,
|
||||
|
||||
//高级查询
|
||||
superQueryButtonRef,
|
||||
superQueryStatus,
|
||||
handleSuperQuery,
|
||||
|
||||
// table区域
|
||||
loading,
|
||||
columns,
|
||||
actionColumn,
|
||||
dataSource,
|
||||
pagination,
|
||||
rowSelection,
|
||||
selectedKeys,
|
||||
tableScroll,
|
||||
enableScrollBar,
|
||||
handleChangeInTable,
|
||||
|
||||
//按钮
|
||||
buttonSwitch,
|
||||
handleAdd,
|
||||
handleEdit,
|
||||
onImportExcel,
|
||||
onExportExcel,
|
||||
cgBIBtnMap,
|
||||
getQueryButtonCfg,
|
||||
getResetButtonCfg,
|
||||
getFormConfirmButtonCfg,
|
||||
cgTopButtonList,
|
||||
cgLinkButtonList,
|
||||
cgButtonJsHandler,
|
||||
cgButtonActionHandler,
|
||||
cgButtonLinkHandler,
|
||||
handleBatchDelete,
|
||||
|
||||
// table-slot
|
||||
downloadRowFile,
|
||||
getImgView,
|
||||
getPcaText,
|
||||
getFormatDate,
|
||||
|
||||
// 操作列
|
||||
getActions,
|
||||
getTreeDropDownActions,
|
||||
|
||||
// 弹窗
|
||||
registerModal,
|
||||
registerCustomModal,
|
||||
registerImportModal,
|
||||
importUrl,
|
||||
handleFormConfig,
|
||||
|
||||
//其他
|
||||
tableReloading,
|
||||
handleSubmitFlow,
|
||||
hrefComponent,
|
||||
viewOnlineCellImage,
|
||||
|
||||
//树特定的配置
|
||||
onlineTreeTableRef,
|
||||
handlerFormSuccess,
|
||||
searchReset,
|
||||
handleExpand,
|
||||
expandedRowKeys,
|
||||
handleExpandedRowsChange,
|
||||
registerDetailModal,
|
||||
handleClickFieldHref,
|
||||
registerBpmModal,
|
||||
|
||||
pageLoading,
|
||||
};
|
||||
//useCompatibleOldVersion(that);
|
||||
return that;
|
||||
},
|
||||
|
||||
// 1引入了loadsh console.log(that.simpleDateFormat(new Date().getTime(),'yyyy-MM-dd'));
|
||||
// 2. value的问题
|
||||
// 3. 变量位置改变后需要 重写api
|
||||
|
||||
// 1添加按钮的时候 预留出样式对象 然后js增强中设置样式对象
|
||||
// 2直接设置css字符串 然后通过js document 往head里面增加css片段 全局生效
|
||||
|
||||
// TODO 清空高级查询
|
||||
// TODO 积木报表打印地址
|
||||
// const reportPrintUrl = ref('')
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
/** [表格主题样式一] 表格强制列不换行 */
|
||||
.j-table-force-nowrap {
|
||||
td,
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ant-table-selection-column {
|
||||
padding: 12px 22px !important;
|
||||
}
|
||||
|
||||
/** 列自适应,弊端会导致列宽失效 */
|
||||
&.ant-table-wrapper .ant-table-content {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.online-cell-image {
|
||||
height: 25px !important;
|
||||
margin: 0 auto;
|
||||
max-width: 80px;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
83
jeecgboot-vue3/src/views/super/online/cgform/cgform.api.ts
Normal file
83
jeecgboot-vue3/src/views/super/online/cgform/cgform.api.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
export enum Api {
|
||||
list = '/online/cgform/head/list',
|
||||
delete = '/online/cgform/head/delete',
|
||||
deleteBatch = '/online/cgform/head/deleteBatch',
|
||||
databaseSync = '/online/cgform/api/doDbSynch',
|
||||
removeRecord = '/online/cgform/head/removeRecord',
|
||||
copyOnline = '/online/cgform/head/copyOnline',
|
||||
copyTable = '/online/cgform/head/copyOnlineTable',
|
||||
|
||||
// CgformModal页面API
|
||||
addAll = '/online/cgform/api/addAll',
|
||||
editAll = '/online/cgform/api/editAll',
|
||||
queryField = '/online/cgform/field/listByHeadId',
|
||||
queryIndex = '/online/cgform/index/listByHeadId',
|
||||
checkOnlyTable = '/online/cgform/api/checkOnlyTable',
|
||||
// 只修改表配置,不改字段
|
||||
editHead = '/online/cgform/head/edit'
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
// 批量移除(移除只会删除表单配置)
|
||||
export const doBatchRemove = (idList: string[]) => doRemove(idList, 0);
|
||||
export const doSingleRemove = (pid) => defHttp.delete({ url: Api.removeRecord, params: { id: pid } },
|
||||
{ joinParamsToUrl: true });
|
||||
// 批量删除(删除会删除对应的数据库表以及子表)
|
||||
export const doBatchDelete = (idList: string[]) => doRemove(idList, 1);
|
||||
export const doSingleDelete = (pid) => defHttp.delete({ url: Api.delete, params: { id: pid } },
|
||||
{ joinParamsToUrl: true });
|
||||
|
||||
// 执行删除操作
|
||||
function doRemove(idList: string[], flag: number) {
|
||||
return defHttp.delete(
|
||||
{
|
||||
url: Api.deleteBatch,
|
||||
params: {
|
||||
ids: idList.join(','),
|
||||
flag: flag,
|
||||
},
|
||||
},
|
||||
{ joinParamsToUrl: true }
|
||||
);
|
||||
}
|
||||
|
||||
// 同步数据库
|
||||
export const doDatabaseSync = (id, method) =>
|
||||
defHttp.post({ url: `${Api.databaseSync}/${id}/${method}`, timeout: 12000, timeoutErrorMessage: '同步数据库超时,已自动刷新' });
|
||||
|
||||
export const doCopyOnlineView = (id) => defHttp.post({ url: `${Api.copyOnline}?code=${id}` });
|
||||
|
||||
/**
|
||||
* 复制表
|
||||
* @param id 要复制的表的id
|
||||
* @param tableName 新的表名
|
||||
* @param params 其他参数
|
||||
*/
|
||||
export const doCopyTable = (id, tableName, params?) => defHttp.get({ url: `${Api.copyTable}/${id}`, params: { tableName, ...params } });
|
||||
|
||||
// 弹窗formApi
|
||||
export const formApi = {
|
||||
// 查询表字段 e3e3NcxzbUiGa53YYVXxWc8ADo5ISgQGx/gaZwERF91oAryDlivjqBv3wqRArgChupi+Y/Gg/swwGEyL0PuVFg==
|
||||
doQueryField: (headId: string, params?) => defHttp.get({ url: Api.queryField, params: { headId, ...params } }),
|
||||
// 查询表index配置
|
||||
doQueryIndexes: (headId: string, params?) => defHttp.get({ url: Api.queryIndex, params: { headId, ...params } }),
|
||||
// 新增或修改
|
||||
doSaveOrUpdate: (params, isUpdate) => {
|
||||
if (isUpdate) {
|
||||
return defHttp.put({ url: Api.editAll, params });
|
||||
} else {
|
||||
return defHttp.post({ url: Api.addAll, params });
|
||||
}
|
||||
},
|
||||
//只是修改表配置不改字段
|
||||
editHead: (params)=>{
|
||||
return defHttp.put({ url: Api.editHead, params });
|
||||
}
|
||||
};
|
||||
308
jeecgboot-vue3/src/views/super/online/cgform/cgform.data.ts
Normal file
308
jeecgboot-vue3/src/views/super/online/cgform/cgform.data.ts
Normal file
@ -0,0 +1,308 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { getDictItemsByCode } from '/@/utils/dict';
|
||||
import { filterDictText } from '/@/utils/dict/JDictSelectUtil';
|
||||
import { buildUUID } from '/@/utils/uuid';
|
||||
|
||||
// 校验失败 flag
|
||||
export const VALIDATE_FAILED = 'validate-failed';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '表类型',
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
dataIndex: 'tableType',
|
||||
width: 140,
|
||||
customRender({ text, record }) {
|
||||
let tableTypeDictOptions = getDictItemsByCode('cgform_table_type');
|
||||
let tbTypeText = filterDictText(tableTypeDictOptions, text);
|
||||
if (record.isTree === 'Y') {
|
||||
tbTypeText += '(树)';
|
||||
}
|
||||
if (record.themeTemplate === 'innerTable') {
|
||||
tbTypeText += '(内嵌)';
|
||||
} else if (record.themeTemplate === 'erp') {
|
||||
tbTypeText += '(ERP)';
|
||||
} else if (record.themeTemplate === 'tab') {
|
||||
tbTypeText += '(TAB)';
|
||||
}
|
||||
return tbTypeText;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '表名',
|
||||
sorter: true,
|
||||
align: 'center',
|
||||
dataIndex: 'tableName',
|
||||
width: 240,
|
||||
},
|
||||
{
|
||||
title: '表描述',
|
||||
align: 'center',
|
||||
dataIndex: 'tableTxt',
|
||||
width: 220,
|
||||
},
|
||||
{
|
||||
title: '版本',
|
||||
align: 'center',
|
||||
dataIndex: 'tableVersion',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '同步状态',
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
dataIndex: 'isDbSynch',
|
||||
slots: { customRender: 'dbSync' },
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
dataIndex: 'createTime',
|
||||
width: 240,
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '表名',
|
||||
field: 'tableName',
|
||||
component: 'JInput',
|
||||
},
|
||||
{
|
||||
label: '表类型',
|
||||
field: 'tableType_MultiString',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'cgform_table_type',
|
||||
mode: 'multiple',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '表描述',
|
||||
field: 'tableTxt',
|
||||
component: 'JInput',
|
||||
},
|
||||
];
|
||||
|
||||
/** 扩展JSON默认值 */
|
||||
export const ExtConfigDefaultJson = {
|
||||
// 对接报表打印
|
||||
reportPrintShow: 0,
|
||||
reportPrintUrl: '',
|
||||
joinQuery: 0,
|
||||
modelFullscreen: 0,
|
||||
modalMinWidth: '',
|
||||
commentStatus: 0,
|
||||
tableFixedAction: 1,
|
||||
tableFixedActionType: 'right',
|
||||
// update-begin--author:liaozhiyang---date:20240329---for:【QQYUN-7872】online表单label较长优化
|
||||
formLabelLengthShow: 0,
|
||||
formLabelLength: null,
|
||||
// update-begin--author:liaozhiyang---date:20240329---for:【QQYUN-7872】online表单label较长优化
|
||||
// 是否启用外部链接
|
||||
enableExternalLink: 0,
|
||||
externalLinkActions: 'add,edit,detail',
|
||||
};
|
||||
|
||||
/** 获取主表的初始化数据 */
|
||||
export function useInitialData() {
|
||||
let initialData = [
|
||||
{
|
||||
dbFieldName: 'id',
|
||||
dbFieldTxt: '主键',
|
||||
dbLength: 36,
|
||||
dbPointLength: 0,
|
||||
dbDefaultVal: '',
|
||||
dbType: 'string',
|
||||
dbIsKey: '1',
|
||||
dbIsNull: '0',
|
||||
// table2
|
||||
isShowForm: '0',
|
||||
isShowList: '0',
|
||||
isReadOnly: '1',
|
||||
fieldShowType: 'text',
|
||||
fieldLength: '200',
|
||||
queryMode: 'single',
|
||||
dbIsSync: '1'
|
||||
},
|
||||
{
|
||||
dbFieldName: 'create_by',
|
||||
dbFieldTxt: '创建人',
|
||||
dbLength: 50,
|
||||
dbPointLength: 0,
|
||||
dbDefaultVal: '',
|
||||
dbType: 'string',
|
||||
dbIsKey: '0',
|
||||
dbIsNull: '1',
|
||||
// table2
|
||||
isShowForm: '0',
|
||||
isShowList: '0',
|
||||
fieldShowType: 'text',
|
||||
fieldLength: '200',
|
||||
queryMode: 'single',
|
||||
dbIsSync: '1'
|
||||
},
|
||||
{
|
||||
dbFieldName: 'create_time',
|
||||
dbFieldTxt: '创建日期',
|
||||
dbLength: 0,
|
||||
dbPointLength: 0,
|
||||
dbDefaultVal: '',
|
||||
dbType: 'Datetime',
|
||||
dbIsKey: '0',
|
||||
dbIsNull: '1',
|
||||
// table2
|
||||
isShowForm: '0',
|
||||
isShowList: '0',
|
||||
fieldShowType: 'datetime',
|
||||
fieldLength: '200',
|
||||
queryMode: 'single',
|
||||
dbIsSync: '1'
|
||||
},
|
||||
{
|
||||
dbFieldName: 'update_by',
|
||||
dbFieldTxt: '更新人',
|
||||
dbLength: 50,
|
||||
dbPointLength: 0,
|
||||
dbDefaultVal: '',
|
||||
dbType: 'string',
|
||||
dbIsKey: '0',
|
||||
dbIsNull: '1',
|
||||
// table2
|
||||
isShowForm: '0',
|
||||
isShowList: '0',
|
||||
fieldShowType: 'text',
|
||||
fieldLength: '200',
|
||||
queryMode: 'single',
|
||||
dbIsSync: '1'
|
||||
},
|
||||
{
|
||||
dbFieldName: 'update_time',
|
||||
dbFieldTxt: '更新日期',
|
||||
dbLength: 0,
|
||||
dbPointLength: 0,
|
||||
dbDefaultVal: '',
|
||||
dbType: 'Datetime',
|
||||
dbIsKey: '0',
|
||||
dbIsNull: '1',
|
||||
// table2
|
||||
isShowForm: '0',
|
||||
isShowList: '0',
|
||||
fieldShowType: 'datetime',
|
||||
fieldLength: '200',
|
||||
queryMode: 'single',
|
||||
dbIsSync: '1'
|
||||
},
|
||||
{
|
||||
dbFieldName: 'sys_org_code',
|
||||
dbFieldTxt: '所属部门',
|
||||
dbLength: 64,
|
||||
dbPointLength: 0,
|
||||
dbDefaultVal: '',
|
||||
dbType: 'string',
|
||||
dbIsKey: '0',
|
||||
dbIsNull: '1',
|
||||
// table2
|
||||
isShowForm: '0',
|
||||
isShowList: '0',
|
||||
fieldShowType: 'text',
|
||||
fieldLength: '200',
|
||||
queryMode: 'single',
|
||||
dbIsSync: '1'
|
||||
},
|
||||
// {
|
||||
// dbFieldName: 'sys_org_code',
|
||||
// dbFieldTxt: '所属部门',
|
||||
// dbLength: 50,
|
||||
// dbPointLength: 0,
|
||||
// dbDefaultVal: '',
|
||||
// dbType: 'string',
|
||||
// dbIsKey: false,
|
||||
// dbIsNull: true
|
||||
// },
|
||||
// {
|
||||
// dbFieldName: 'sys_company_code',
|
||||
// dbFieldTxt: '所属公司',
|
||||
// dbLength: 50,
|
||||
// dbPointLength: 0,
|
||||
// dbDefaultVal: '',
|
||||
// dbType: 'string',
|
||||
// dbIsKey: false,
|
||||
// dbIsNull: true
|
||||
// },
|
||||
// {
|
||||
// dbFieldName: 'bpm_status',
|
||||
// dbFieldTxt: '流程状态',
|
||||
// dbLength: 32,
|
||||
// dbPointLength: 0,
|
||||
// dbDefaultVal: '',
|
||||
// dbType: 'string',
|
||||
// dbIsKey: false,
|
||||
// dbIsNull: true
|
||||
// }
|
||||
];
|
||||
// 临时 id,不保存到数据库
|
||||
let tempIds: string[] = [];
|
||||
initialData.forEach((record) => {
|
||||
record['id'] = buildUUID();
|
||||
tempIds.push(record['id']);
|
||||
});
|
||||
return { initialData, tempIds };
|
||||
}
|
||||
|
||||
/** 获取树的初始化数据 */
|
||||
export function useTreeNeedFields() {
|
||||
return [
|
||||
{
|
||||
dbFieldName: 'pid',
|
||||
dbFieldTxt: '父级节点',
|
||||
dbLength: 32,
|
||||
dbPointLength: 0,
|
||||
dbDefaultVal: '',
|
||||
dbType: 'string',
|
||||
dbIsKey: '0',
|
||||
dbIsNull: '1',
|
||||
// table2
|
||||
isShowForm: '1',
|
||||
isShowList: '0',
|
||||
fieldShowType: 'text',
|
||||
fieldLength: '200',
|
||||
queryMode: 'single',
|
||||
dbIsSync: '1'
|
||||
},
|
||||
{
|
||||
dbFieldName: 'has_child',
|
||||
dbFieldTxt: '是否有子节点',
|
||||
dbLength: 3,
|
||||
dbPointLength: 0,
|
||||
dbDefaultVal: '',
|
||||
dbType: 'string',
|
||||
dbIsKey: '0',
|
||||
dbIsNull: '1',
|
||||
// table2
|
||||
isShowForm: '0',
|
||||
isShowList: '0',
|
||||
fieldShowType: 'list',
|
||||
fieldLength: '200',
|
||||
queryMode: 'single',
|
||||
// table3
|
||||
dictField: 'yn',
|
||||
dbIsSync: '1'
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* online 默认按钮
|
||||
*/
|
||||
export const onlineDefaultButton = [
|
||||
{ code: 'add', title: '新增', status: 0 },
|
||||
{ code: 'edit', title: '编辑', status: 0 },
|
||||
{ code: 'delete', title: '删除', status: 0 },
|
||||
{ code: 'export', title: '导出', status: 0 },
|
||||
{ code: 'import', title: '导入', status: 0 },
|
||||
{ code: 'query', title: '查询', status: 0 },
|
||||
];
|
||||
@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
:height="180"
|
||||
:title="title"
|
||||
:width="600"
|
||||
:maskClosable="false"
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
:footer="null"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<div class="aiWrap">
|
||||
<div class="titleArea">
|
||||
<svg
|
||||
t="1707100353985"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="4235"
|
||||
width="26"
|
||||
height="26"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.8 64 64 264.8 64 512s200.8 448 448 448 448-200.8 448-448S759.2 64 512 64z m32 704h-64v-64h64v64z m11.2-203.2l-5.6 4.8c-3.2 2.4-5.6 8-5.6 12.8v58.4h-64v-58.4c0-24.8 11.2-48 29.6-63.2l5.6-4.8c56-44.8 83.2-68 83.2-108C598.4 358.4 560 320 512 320c-49.6 0-86.4 36.8-86.4 86.4h-64C361.6 322.4 428 256 512 256c83.2 0 150.4 67.2 150.4 150.4 0 72.8-49.6 112.8-107.2 158.4z"
|
||||
p-id="4236"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
<h3>创建工作表字段需要专业建议?试试AI智能推荐吧</h3>
|
||||
</div>
|
||||
<p class="tip">可尝试添加修饰词,如:智能家居行业的生产计划表</p>
|
||||
<div class="content">
|
||||
<a-input v-model:value.trim="inputValue" placeholder="请输入修饰词" /><a-button :loading="loading" type="primary" @click="handleCreate"
|
||||
>生成表</a-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { ref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
const configUrl = {
|
||||
aigc: '/online/cgform/api/aigc',
|
||||
};
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const [registerModal, { closeModal }] = useModalInner();
|
||||
const { createMessage } = useMessage();
|
||||
const inputValue = ref('');
|
||||
const loading = ref(false);
|
||||
const title = ref('ai创建表');
|
||||
|
||||
const handleCancel = () => {
|
||||
closeModal();
|
||||
};
|
||||
const handleCreate = () => {
|
||||
if (inputValue.value.trim() === '') {
|
||||
createMessage.warning('请输入修饰词~');
|
||||
} else {
|
||||
loading.value = true;
|
||||
defHttp
|
||||
// timeout:超时时间 5 分钟
|
||||
.post({ url: `${configUrl.aigc}?prompt=${inputValue.value}`, timeout: 1e3 * 60 * 5 })
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
handleCancel();
|
||||
emit('success');
|
||||
inputValue.value = '';
|
||||
})
|
||||
.catch((err) => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
html[data-theme="light"] {
|
||||
.aiWrap {
|
||||
h3 {color: #333;}
|
||||
.tip { color: #666;}
|
||||
}
|
||||
}
|
||||
.aiWrap {
|
||||
padding: 30px 20px 20px;
|
||||
.titleArea {
|
||||
position: relative;
|
||||
color: #ffb308;
|
||||
width: fit-content;
|
||||
margin:0 auto;
|
||||
svg {
|
||||
position: absolute;
|
||||
left: -30px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
h3 {
|
||||
text-align: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.tip {
|
||||
text-align: center;
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
}
|
||||
.ant-btn {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,232 @@
|
||||
<!-- Online表单,配置地址弹窗 -->
|
||||
<template>
|
||||
<BasicModal
|
||||
@register="registerModal"
|
||||
title="配置地址"
|
||||
:width="750"
|
||||
:canFullscreen="false"
|
||||
:showOkBtn="false"
|
||||
cancelText="关闭"
|
||||
>
|
||||
<div class="content">
|
||||
<a-collapse v-model:activeKey="collapseKey" class="j-collapse" :bordered="false" ghost>
|
||||
<a-collapse-panel key="def" header="配置地址" class="j-collapse-panel no-header">
|
||||
<a-row style="margin-bottom: 8px">
|
||||
<a-col :span="24">
|
||||
<a-input :readOnly="true" addonBefore="数据列表地址" :value="getAddress.list">
|
||||
<template #addonAfter>
|
||||
<a :href="getAddress.list" target="_blank">打开</a>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-button class="copy-sql" type="primary" size="small" @click="copySqlClick">复制菜单SQL</a-button>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel v-if="enableExternalLink" key="external" header="外部链接">
|
||||
<a-row style="margin-bottom: 8px">
|
||||
<a-col :span="24">
|
||||
<a-input :readOnly="true" addonBefore="外部新增地址" :value="getAddress.extLink.add">
|
||||
<template #addonAfter>
|
||||
<a :href="getAddress.extLink.add + '?token=' + accessToken" target="_blank">打开</a>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row style="margin-bottom: 8px">
|
||||
<a-col :span="24">
|
||||
<a-input :readOnly="true" addonBefore="外部修改地址" :value="getAddress.extLink.edit">
|
||||
<template #addonAfter>
|
||||
<a @click="openPage(getAddress.extLink.edit)">打开</a>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row style="margin-bottom: 8px">
|
||||
<a-col :span="24">
|
||||
<a-input :readOnly="true" addonBefore="外部详情地址" :value="getAddress.extLink.detail">
|
||||
<template #addonAfter>
|
||||
<a @click="openPage(getAddress.extLink.detail)">打开</a>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div style="text-align: right; color: red">注意:<span style="font-weight: bold">{dataId}</span> 为数据id</div>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</div>
|
||||
</BasicModal>
|
||||
|
||||
<!-- 让用户输入 dataId 的弹窗 -->
|
||||
<BasicModal v-model:visible="dataIdProps.visible" v-bind="dataIdBind">
|
||||
<a-input placeholder="请输入dataId" v-model:value="dataIdProps.value"></a-input>
|
||||
<template #footer>
|
||||
<a :href="dataIdHref" target="_blank">
|
||||
<a-button type="primary" @click="onClickDataId">确定</a-button>
|
||||
</a>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, reactive, ref} from "vue";
|
||||
import {BasicModal, useModalInner} from "@/components/Modal";
|
||||
import {parseExtConfigJson} from "../util/utils";
|
||||
import {copyTextToClipboard} from "@/hooks/web/useCopyToClipboard";
|
||||
import {getToken} from "@/utils/auth";
|
||||
import {useMessage} from "@/hooks/web/useMessage";
|
||||
import {buildUUID} from "@/utils/uuid";
|
||||
const emit = defineEmits(['register'])
|
||||
const {createMessage: $message} = useMessage();
|
||||
|
||||
const accessToken = computed(() => getToken());
|
||||
|
||||
const collapseKey = ref(['def'])
|
||||
const enableExternalLink = ref(false);
|
||||
const themeTemplate = ref('normal');
|
||||
const isTree = ref(false);
|
||||
const model = reactive({
|
||||
title: '',
|
||||
content: '',
|
||||
copyText: '',
|
||||
copyTitle: '',
|
||||
formId: '',
|
||||
})
|
||||
|
||||
const [registerModal] = useModalInner((data: Recordable) => {
|
||||
Object.assign(model, data, {
|
||||
formId: data.record.id,
|
||||
})
|
||||
// 解析扩展JSON
|
||||
const extConfigJson = parseExtConfigJson(data.record);
|
||||
|
||||
enableExternalLink.value = extConfigJson.enableExternalLink === 1;
|
||||
themeTemplate.value = data.record.themeTemplate;
|
||||
isTree.value = data.record.isTree === 'Y';
|
||||
if (enableExternalLink.value) {
|
||||
collapseKey.value = ['def', 'external'];
|
||||
} else {
|
||||
collapseKey.value = ['def'];
|
||||
}
|
||||
});
|
||||
|
||||
const getAddress = computed(() => {
|
||||
|
||||
const extLink: Recordable = {}
|
||||
if (enableExternalLink.value) {
|
||||
const before = `/online/cgform/share/${model.formId}`;
|
||||
extLink.add = `${before}/add`;
|
||||
extLink.edit = `${before}/u/{dataId}`;
|
||||
extLink.detail = `${before}/d/{dataId}`;
|
||||
}
|
||||
|
||||
return {
|
||||
list: model.content,
|
||||
extLink,
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* 复制sql
|
||||
*/
|
||||
function copySqlClick() {
|
||||
// update-begin--author:liaozhiyang---date:20240308---for:【QQYUN-12348】online生成的菜单sql 自动带上组件名称
|
||||
let component_name = 'OnlineAutoList';
|
||||
if (themeTemplate.value === 'normal') {
|
||||
component_name = 'OnlineAutoList';
|
||||
} else if (themeTemplate.value === 'erp') {
|
||||
component_name = 'CgformErpList';
|
||||
} else if (themeTemplate.value === 'innerTable') {
|
||||
component_name = 'OnlCgformInnerTableList';
|
||||
} else if (themeTemplate.value === 'tab') {
|
||||
component_name = 'OnlCgformTabList';
|
||||
}
|
||||
if (isTree.value) {
|
||||
component_name = 'DefaultOnlineList';
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240308---for:【QQYUN-12348】online生成的菜单sql 自动带上组件名称
|
||||
const insertMenuSql = `-- 插入菜单
|
||||
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external)
|
||||
VALUES ('${buildUUID()}', NULL, '${model.copyTitle}', '${model.copyText}', '1', '${component_name}', NULL, 0, NULL, '1', 0.00, 0, NULL, 0, 1, 0, 0, 0, NULL, '1', 0, 0, 'admin', null, NULL, NULL, 0)
|
||||
`;
|
||||
copyText(insertMenuSql);
|
||||
}
|
||||
|
||||
// 复制文本到剪贴板
|
||||
function copyText(text: string) {
|
||||
const success = copyTextToClipboard(text)
|
||||
if (success) {
|
||||
$message.success('复制成功!')
|
||||
} else {
|
||||
$message.error('复制失败!')
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
const dataIdProps = reactive({
|
||||
base: '',
|
||||
value: '',
|
||||
visible: false,
|
||||
});
|
||||
const dataIdHref = computed(() => {
|
||||
return dataIdProps.value ? dataIdProps.base.replace(/{dataId}/, dataIdProps.value) + '?token=' + accessToken.value : undefined;
|
||||
});
|
||||
const dataIdBind = computed(() => {
|
||||
return {
|
||||
title: '请输入dataId',
|
||||
minHeight: 120,
|
||||
centered: true,
|
||||
canFullscreen: false,
|
||||
onOk: () => (dataIdProps.visible = false),
|
||||
onCancel: () => (dataIdProps.visible = false),
|
||||
};
|
||||
});
|
||||
|
||||
function openPage(url: string) {
|
||||
dataIdProps.base = url;
|
||||
dataIdProps.value = '';
|
||||
dataIdProps.visible = true;
|
||||
}
|
||||
|
||||
function onClickDataId() {
|
||||
if (!dataIdProps.value) {
|
||||
$message.warn('请输入dataId');
|
||||
return
|
||||
}
|
||||
dataIdProps.visible = false;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.j-collapse {
|
||||
//background: red;
|
||||
:deep(.ant-collapse-header) {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
:deep(.ant-collapse-content-box) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.j-collapse-panel {
|
||||
&.no-header {
|
||||
:deep(.ant-collapse-header) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.copy-sql {
|
||||
float: right;
|
||||
margin-top: 6px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="cgf-item">
|
||||
<label class="cgf-label">
|
||||
<span v-if="required" class="cgf-required">*</span>{{ label }}
|
||||
</label>
|
||||
<a-input
|
||||
v-if="type === 'input'"
|
||||
:value="modelValue"
|
||||
:disabled="disabled"
|
||||
:allow-clear="allowClear"
|
||||
:status="status"
|
||||
style="width: 100%"
|
||||
@update:value="emit('update:modelValue', $event)"
|
||||
@change="emit('change', $event)"
|
||||
/>
|
||||
<a-input-number
|
||||
v-else-if="type === 'input-number'"
|
||||
:value="modelValue"
|
||||
:disabled="disabled"
|
||||
style="width: 100%"
|
||||
@update:value="emit('update:modelValue', $event)"
|
||||
@change="emit('change', $event)"
|
||||
/>
|
||||
<a-select
|
||||
v-else-if="type === 'select'"
|
||||
:value="modelValue"
|
||||
:options="options"
|
||||
:allow-clear="false"
|
||||
:disabled="disabled"
|
||||
:status="status"
|
||||
style="width: 100%"
|
||||
@update:value="emit('update:modelValue', $event)"
|
||||
@change="emit('change', $event)"
|
||||
/>
|
||||
<a-radio-group
|
||||
v-else-if="type === 'radio-group'"
|
||||
:value="modelValue"
|
||||
:options="options"
|
||||
:disabled="disabled"
|
||||
@update:value="emit('update:modelValue', $event)"
|
||||
@change="emit('change', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
label: string;
|
||||
type: 'input' | 'select' | 'input-number' | 'radio-group';
|
||||
modelValue?: any;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
allowClear?: boolean;
|
||||
options?: { label: string; value: any }[];
|
||||
status?: '' | 'error';
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: any): void;
|
||||
(e: 'change', value: any): void;
|
||||
}>();
|
||||
</script>
|
||||
@ -0,0 +1,533 @@
|
||||
<template>
|
||||
<div class="cgform-fields" :class="{ 'cgform-fields--inline': isInlineLayout }">
|
||||
<CgformFieldItem
|
||||
v-for="item in baseFields"
|
||||
:key="item.field"
|
||||
:label="item.label"
|
||||
:type="item.type"
|
||||
:model-value="(formModel as Recordable)[item.field]"
|
||||
:required="item.required"
|
||||
:disabled="item.disabled"
|
||||
:allow-clear="item.allowClear"
|
||||
:options="item.options"
|
||||
:status="item.status"
|
||||
@update:model-value="(formModel as Recordable)[item.field] = $event"
|
||||
@change="item.onChange?.($event)"
|
||||
/>
|
||||
<template v-if="[1, 2].includes(formModel.tableType)">
|
||||
<!-- 非附表条件字段(单表/主表) -->
|
||||
<CgformFieldItem
|
||||
v-for="item in conditionalFields"
|
||||
v-show="item.show !== false"
|
||||
:key="item.field"
|
||||
:label="item.label"
|
||||
:type="item.type"
|
||||
:model-value="(formModel as Recordable)[item.field]"
|
||||
:required="item.required"
|
||||
:disabled="item.disabled"
|
||||
:allow-clear="item.allowClear"
|
||||
:options="item.options"
|
||||
:status="item.status"
|
||||
@update:model-value="(formModel as Recordable)[item.field] = $event"
|
||||
@change="item.onChange?.($event)"
|
||||
/>
|
||||
<!-- 非附表类型按钮 -->
|
||||
<div v-if="formModel.tableType !== 3" class="cgf-item cgf-item--buttons">
|
||||
<a-button preIcon="ant-design:setting" @click="onOpenExtConfig">扩展配置</a-button>
|
||||
<a-button type="link" :preIcon="expandStatus ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" @click="expandStatus = !expandStatus">{{
|
||||
expandStatus ? '收起' : '展开'
|
||||
}}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="formModel.tableType === 3">
|
||||
<!-- 附表类型展开:row2=显示复选框+表单风格+关联类型+序号 -->
|
||||
<template v-if="expandStatus">
|
||||
<CgformFieldItem
|
||||
v-for="item in preGroupFields"
|
||||
:key="item.field"
|
||||
:label="item.label"
|
||||
:type="item.type"
|
||||
:model-value="(formModel as Recordable)[item.field]"
|
||||
:options="item.options"
|
||||
@update:model-value="(formModel as Recordable)[item.field] = $event"
|
||||
/>
|
||||
<div class="cgf-item cgf-item--group">
|
||||
<div class="cgf-item--group-left">
|
||||
<a-radio-group v-model:value="formModel.relationType" :options="relationTypeOptions" @change="onRelationTypeChange" />
|
||||
<label class="cgf-label cgf-label--inline">序号</label>
|
||||
<a-input-number v-model:value="formModel.tabOrderNum" />
|
||||
</div>
|
||||
<div class="cgf-item--group-right">
|
||||
<a-button preIcon="ant-design:setting" :disabled="formModel.relationType === 1" @click="onOpenExtConfig">扩展配置</a-button>
|
||||
<a-button type="link" preIcon="ant-design:up-outlined" @click="expandStatus = !expandStatus">收起</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<CgformFieldItem
|
||||
v-for="item in postGroupFields"
|
||||
:key="item.field"
|
||||
:label="item.label"
|
||||
:type="item.type"
|
||||
:model-value="(formModel as Recordable)[item.field]"
|
||||
:options="item.options"
|
||||
@update:model-value="(formModel as Recordable)[item.field] = $event"
|
||||
/>
|
||||
</template>
|
||||
<!-- 附表类型收起:col1+col2空格 + col3全部内容-->
|
||||
<template v-else>
|
||||
<div class="cgf-spacer"></div>
|
||||
<div class="cgf-spacer"></div>
|
||||
<div class="cgf-item cgf-item--group">
|
||||
<div class="cgf-item--group-left">
|
||||
<a-radio-group v-model:value="formModel.relationType" :options="relationTypeOptions" @change="onRelationTypeChange" />
|
||||
<label class="cgf-label cgf-label--inline">序号</label>
|
||||
<a-input-number v-model:value="formModel.tabOrderNum" style="width: 50px" />
|
||||
</div>
|
||||
<div class="cgf-item--group-right">
|
||||
<a-button preIcon="ant-design:setting" :disabled="formModel.relationType === 1" @click="onOpenExtConfig">扩展配置</a-button>
|
||||
<a-button type="link" preIcon="ant-design:down-outlined" @click="expandStatus = !expandStatus">展开</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
<ExtendConfigModal @register="registerExtendConfigModal" :parentForm="formActionForModal" @ok="onExtConfigOk" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref, toRaw } from 'vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { duplicateValidate } from '/@/utils/helper/validator';
|
||||
import ExtendConfigModal from './ExtendConfigModal.vue';
|
||||
import CgformFieldItem from './CgformFieldItem.vue';
|
||||
import { ExtConfigDefaultJson } from '../cgform.data';
|
||||
import { parseExtConfigJson } from '../util/utils';
|
||||
|
||||
interface FieldItem {
|
||||
label: string;
|
||||
field: string;
|
||||
type: 'input' | 'select' | 'input-number' | 'radio-group';
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
allowClear?: boolean;
|
||||
options?: { label: string; value: any }[];
|
||||
onChange?: (value: any) => void;
|
||||
show?: boolean;
|
||||
status?: '' | 'error';
|
||||
}
|
||||
|
||||
const fieldErrors = reactive<Record<string, '' | 'error'>>({
|
||||
tableName: '',
|
||||
tableTxt: '',
|
||||
treeFieldname: '',
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'tableTypeChange', value: number): void;
|
||||
(e: 'relationTypeChange', value: number): void;
|
||||
(e: 'isTreeChange', value: string): void;
|
||||
(e: 'extConfigSaved', values: Recordable): void;
|
||||
}>();
|
||||
|
||||
const { createMessage: $message } = useMessage();
|
||||
|
||||
const expandStatus = ref(false);
|
||||
|
||||
const tableTypeOptions = [
|
||||
{ label: '单表', value: 1 },
|
||||
{ label: '主表', value: 2 },
|
||||
{ label: '附表', value: 3 },
|
||||
];
|
||||
const relationTypeOptions = [
|
||||
{ label: '一对多', value: 0 },
|
||||
{ label: '一对一', value: 1 },
|
||||
];
|
||||
const isCheckboxOptions = [
|
||||
{ label: '显示', value: 'Y' },
|
||||
{ label: '不显示', value: 'N' },
|
||||
];
|
||||
const themeTemplateOptions = [
|
||||
{ label: '默认主题', value: 'normal' },
|
||||
{ label: 'ERP主题(一对多)', value: 'erp' },
|
||||
{ label: '内嵌子表主题(一对多)', value: 'innerTable' },
|
||||
{ label: 'TAB主题(一对多)', value: 'tab' },
|
||||
];
|
||||
const formTemplateOptions = [
|
||||
{ label: '一列', value: '1' },
|
||||
{ label: '两列', value: '2' },
|
||||
{ label: '三列', value: '3' },
|
||||
{ label: '四列', value: '4' },
|
||||
];
|
||||
const scrollOptions = [
|
||||
{ label: '有', value: 1 },
|
||||
{ label: '无', value: 0 },
|
||||
];
|
||||
const isPageOptions = [
|
||||
{ label: '显示', value: 'Y' },
|
||||
{ label: '不显示', value: 'N' },
|
||||
];
|
||||
const isTreeOptions = [
|
||||
{ label: '是', value: 'Y' },
|
||||
{ label: '否', value: 'N' },
|
||||
];
|
||||
|
||||
const DEFAULT_FORM_MODEL = {
|
||||
id: '',
|
||||
tableVersion: null as any,
|
||||
tableName: '',
|
||||
tableTxt: '',
|
||||
tableType: 1,
|
||||
relationType: 0,
|
||||
tabOrderNum: null as any,
|
||||
idSequence: '',
|
||||
isCheckbox: 'Y',
|
||||
themeTemplate: 'normal',
|
||||
formTemplate: '1',
|
||||
scroll: 1,
|
||||
isPage: 'Y',
|
||||
isTree: 'N',
|
||||
treeParentIdField: '',
|
||||
treeIdField: '',
|
||||
treeFieldname: '',
|
||||
subTableStr: '',
|
||||
extConfigJson: '',
|
||||
};
|
||||
const formModel = reactive({ ...DEFAULT_FORM_MODEL });
|
||||
|
||||
function onTableTypeChange(value: number) {
|
||||
if (value === 1) {
|
||||
formModel.themeTemplate = 'normal';
|
||||
} else if (value === 3) {
|
||||
formModel.isTree = 'N';
|
||||
emit('isTreeChange', 'N');
|
||||
}
|
||||
emit('tableTypeChange', value);
|
||||
}
|
||||
|
||||
function onRelationTypeChange(e: any) {
|
||||
const value = e?.target?.value ?? e;
|
||||
emit('relationTypeChange', value);
|
||||
}
|
||||
|
||||
function onIsTreeChange(value: string) {
|
||||
emit('isTreeChange', value);
|
||||
}
|
||||
|
||||
let _tableNameLastMsg = '';
|
||||
function validateTableNameSync(val: string) {
|
||||
let msg = '';
|
||||
if (!val) {
|
||||
fieldErrors.tableName = 'error';
|
||||
} else if (/[\u4E00-\u9FA5]/g.test(val)) {
|
||||
fieldErrors.tableName = 'error';
|
||||
msg = '表名不允许输入中文';
|
||||
} else if (val.length > 50) {
|
||||
fieldErrors.tableName = 'error';
|
||||
msg = '表名最长50个字符';
|
||||
} else {
|
||||
fieldErrors.tableName = '';
|
||||
}
|
||||
if (msg && msg !== _tableNameLastMsg) {
|
||||
$message.warning(msg);
|
||||
}
|
||||
_tableNameLastMsg = msg;
|
||||
}
|
||||
|
||||
function validateTableTxtSync(val: string) {
|
||||
fieldErrors.tableTxt = !val || val.length > 200 ? 'error' : '';
|
||||
}
|
||||
|
||||
// 基础字段(始终显示)
|
||||
const baseFields = computed<FieldItem[]>(() => [
|
||||
{
|
||||
label: '表名',
|
||||
field: 'tableName',
|
||||
type: 'input',
|
||||
required: true,
|
||||
disabled: !!(formModel.tableVersion && formModel.tableVersion != 1),
|
||||
allowClear: true,
|
||||
status: fieldErrors.tableName,
|
||||
onChange: (e: any) => validateTableNameSync(e?.target?.value ?? e ?? ''),
|
||||
},
|
||||
{
|
||||
label: '表描述',
|
||||
field: 'tableTxt',
|
||||
type: 'input',
|
||||
required: true,
|
||||
allowClear: true,
|
||||
status: fieldErrors.tableTxt,
|
||||
onChange: (e: any) => validateTableTxtSync(e?.target?.value ?? e ?? ''),
|
||||
},
|
||||
{
|
||||
label: '表类型',
|
||||
field: 'tableType',
|
||||
type: 'select',
|
||||
options: tableTypeOptions,
|
||||
onChange: onTableTypeChange,
|
||||
},
|
||||
]);
|
||||
|
||||
// 附表字段
|
||||
const subTableField: FieldItem = {
|
||||
label: '附表',
|
||||
field: 'subTableStr',
|
||||
type: 'input',
|
||||
disabled: true,
|
||||
};
|
||||
|
||||
// 关联类型字段
|
||||
// const relationTypeField: FieldItem = {
|
||||
// label: '关联类型',
|
||||
// field: 'relationType',
|
||||
// type: 'radio-group',
|
||||
// options: relationTypeOptions,
|
||||
// onChange: onRelationTypeChange,
|
||||
// };
|
||||
|
||||
// 序号字段
|
||||
// const tabOrderNumField: FieldItem = {
|
||||
// label: '序号',
|
||||
// field: 'tabOrderNum',
|
||||
// type: 'input-number',
|
||||
// };
|
||||
|
||||
// 展开后的通用字段
|
||||
const expandCommonFields = computed<FieldItem[]>(() => [
|
||||
{ label: '复选框', field: 'isCheckbox', type: 'select', options: isCheckboxOptions },
|
||||
{ label: '表单风格', field: 'formTemplate', type: 'select', options: formTemplateOptions },
|
||||
{ label: '滚动条', field: 'scroll', type: 'select', options: scrollOptions },
|
||||
{ label: '分页', field: 'isPage', type: 'select', options: isPageOptions },
|
||||
]);
|
||||
|
||||
// 条件字段,根据 tableType 和 expandStatus 动态变化
|
||||
const conditionalFields = computed<FieldItem[]>(() => {
|
||||
const { tableType, isTree } = formModel;
|
||||
const expanded = expandStatus.value;
|
||||
|
||||
// 附表 (tableType === 3):完全由模板中的 preGroupFields/postGroupFields 处理
|
||||
if (tableType === 3) return [];
|
||||
|
||||
// 主表 (tableType === 2)
|
||||
if (tableType === 2) {
|
||||
const fields: FieldItem[] = [{ ...subTableField, show: !!formModel.subTableStr }];
|
||||
if (expanded) {
|
||||
fields.push(...expandCommonFields.value, { label: '主题模板', field: 'themeTemplate', type: 'select', options: themeTemplateOptions });
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
// 单表 (tableType === 1)
|
||||
if (expanded) {
|
||||
const fields: FieldItem[] = [
|
||||
...expandCommonFields.value,
|
||||
{ label: '是否树', field: 'isTree', type: 'select', options: isTreeOptions, onChange: onIsTreeChange },
|
||||
];
|
||||
if (isTree === 'Y') {
|
||||
fields.push(
|
||||
{ label: '树父ID', field: 'treeParentIdField', type: 'input' },
|
||||
{ label: '树表单列', field: 'treeFieldname', type: 'input', required: true, status: fieldErrors.treeFieldname }
|
||||
);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
// 附表展开:关联类型+序号 group 前面的字段(isCheckbox, formTemplate → row2 col1-2)
|
||||
const preGroupFields = computed<FieldItem[]>(() => {
|
||||
if (formModel.tableType !== 3 || !expandStatus.value) return [];
|
||||
return expandCommonFields.value.slice(0, 2);
|
||||
});
|
||||
|
||||
// 附表展开:关联类型+序号 group 后面的字段(scroll, isPage → row3 col1-2)
|
||||
const postGroupFields = computed<FieldItem[]>(() => {
|
||||
if (formModel.tableType !== 3 || !expandStatus.value) return [];
|
||||
return expandCommonFields.value.slice(2);
|
||||
});
|
||||
|
||||
// 按钮是否与基础字段在同一行(无条件字段时)
|
||||
const isInlineLayout = computed(() => {
|
||||
if (expandStatus.value) return false;
|
||||
if (formModel.tableType === 1) return true;
|
||||
if (formModel.tableType === 2 && !formModel.subTableStr) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
function resetFields() {
|
||||
Object.assign(formModel, DEFAULT_FORM_MODEL);
|
||||
fieldErrors.tableName = '';
|
||||
fieldErrors.tableTxt = '';
|
||||
}
|
||||
|
||||
function setFieldsValue(values: Recordable) {
|
||||
Object.assign(formModel, values);
|
||||
}
|
||||
|
||||
function getFieldsValue(fields?: string[]) {
|
||||
if (fields) {
|
||||
return Object.fromEntries(fields.map((f) => [f, (formModel as Recordable)[f]]));
|
||||
}
|
||||
return { ...toRaw(formModel) };
|
||||
}
|
||||
|
||||
async function validate() {
|
||||
fieldErrors.tableName = '';
|
||||
fieldErrors.tableTxt = '';
|
||||
fieldErrors.treeFieldname = '';
|
||||
if (!formModel.tableName) {
|
||||
fieldErrors.tableName = 'error';
|
||||
return Promise.reject('请输入表名');
|
||||
}
|
||||
if (/[\u4E00-\u9FA5]/g.test(formModel.tableName)) {
|
||||
fieldErrors.tableName = 'error';
|
||||
return Promise.reject('表名不允许输入中文');
|
||||
}
|
||||
if (formModel.tableName.length > 50) {
|
||||
fieldErrors.tableName = 'error';
|
||||
return Promise.reject('表名最长50个字符');
|
||||
}
|
||||
try {
|
||||
await duplicateValidate('onl_cgform_head', 'table_name', formModel.tableName, formModel.id);
|
||||
} catch (e: any) {
|
||||
fieldErrors.tableName = 'error';
|
||||
return Promise.reject('表名已存在');
|
||||
}
|
||||
if (!formModel.tableTxt) {
|
||||
fieldErrors.tableTxt = 'error';
|
||||
return Promise.reject('请输入表描述');
|
||||
}
|
||||
if (formModel.tableTxt.length > 200) {
|
||||
fieldErrors.tableTxt = 'error';
|
||||
return Promise.reject('表描述最长200个字');
|
||||
}
|
||||
if (formModel.isTree === 'Y' && formModel.tableType === 1 && !formModel.treeFieldname) {
|
||||
fieldErrors.treeFieldname = 'error';
|
||||
expandStatus.value = true;
|
||||
return Promise.reject('请填写树表单列');
|
||||
}
|
||||
if (extConfigJson.joinQuery && ['erp'].includes(formModel.themeTemplate)) {
|
||||
return Promise.reject('ERP不支持联合查询功能');
|
||||
}
|
||||
if (extConfigJson.joinQuery && ['innerTable'].includes(formModel.themeTemplate)) {
|
||||
return Promise.reject('内嵌子表不支持联合查询功能');
|
||||
}
|
||||
return getFieldsValue();
|
||||
}
|
||||
|
||||
// 扩展配置 JSON
|
||||
let extConfigJson: Recordable = {};
|
||||
const [registerExtendConfigModal, extendConfigModal] = useModal();
|
||||
|
||||
// ExtendConfigModal 需要的 parentForm 接口
|
||||
const formActionForModal = {
|
||||
getFieldsValue,
|
||||
setFieldsValue,
|
||||
validateFields: (_fields?: string[]) => Promise.resolve(),
|
||||
};
|
||||
|
||||
function initialExtConfigJson(record: Recordable) {
|
||||
const parseJSON = parseExtConfigJson(record);
|
||||
extConfigJson = Object.assign({}, ExtConfigDefaultJson, parseJSON, {
|
||||
isDesForm: record.isDesForm || 'N',
|
||||
desFormCode: record.desFormCode || '',
|
||||
});
|
||||
}
|
||||
|
||||
function getExtConfigJson() {
|
||||
return extConfigJson;
|
||||
}
|
||||
|
||||
function onOpenExtConfig() {
|
||||
extendConfigModal.openModal(true, { extConfigJson });
|
||||
}
|
||||
|
||||
async function onExtConfigOk(values: Recordable) {
|
||||
extConfigJson = values;
|
||||
emit('extConfigSaved', values);
|
||||
}
|
||||
|
||||
defineExpose({ formModel, resetFields, setFieldsValue, getFieldsValue, validate, initialExtConfigJson, getExtConfigJson });
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.cgform-fields {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-top: 5px;
|
||||
:deep(.cgf-item) {
|
||||
width: 33.333%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
.cgf-label {
|
||||
flex-shrink: 0;
|
||||
width: 75px;
|
||||
text-align: right;
|
||||
padding-right: 8px;
|
||||
font-size: 14px;
|
||||
.cgf-required {
|
||||
color: #ff4d4f;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cgf-item--buttons {
|
||||
flex: 1;
|
||||
min-width: 33.333%;
|
||||
justify-content: flex-end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
// 附表收起时固定宽度(不 flex-grow)
|
||||
&-fixed {
|
||||
flex: 0 0 33.333%;
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
.cgf-spacer {
|
||||
width: 33.333%;
|
||||
}
|
||||
.cgf-item--group {
|
||||
width: 33.333%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 4px 8px;
|
||||
padding-left: 34px;
|
||||
.cgf-label {
|
||||
width: 50px;
|
||||
}
|
||||
.cgf-item--group-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.cgf-item--group-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
// 无条件字段时,按钮与基础字段同行(单表收起 / 主表无附表收起)
|
||||
&--inline {
|
||||
flex-wrap: nowrap;
|
||||
:deep(.cgf-item) {
|
||||
flex: 1 0 0;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
.cgf-item--buttons {
|
||||
flex: 0 0 auto;
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.ant-btn) {
|
||||
&.ant-btn-link {
|
||||
margin-right: -5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
& > .anticon + span {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,879 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
ref="modalRef"
|
||||
:title="title"
|
||||
:width="1200"
|
||||
:maskClosable="false"
|
||||
:defaultFullscreen="true"
|
||||
:confirmLoading="confirmLoading"
|
||||
v-bind="$attrs"
|
||||
wrapClassName="onlForm-config-modal"
|
||||
@cancel="onCancel"
|
||||
@register="registerModal"
|
||||
>
|
||||
<div ref="onlFormContentRef" class="onlForm-content">
|
||||
<a-spin ref="spinRef" wrapperClassName="pl-2 pr-2" :spinning="confirmLoading">
|
||||
<div ref="onlFormContentFormRef" class="onlForm-content-form">
|
||||
<CgformHeadForm
|
||||
ref="cgformHeadFormRef"
|
||||
@tableTypeChange="onTableTypeChange"
|
||||
@relationTypeChange="onRelationTypeChange"
|
||||
@isTreeChange="onIsTreeChange"
|
||||
@extConfigSaved="onExtConfigSaved"
|
||||
/>
|
||||
</div>
|
||||
<a-spin :spinning="tableLoading || hideTabs">
|
||||
<a-tabs v-show="!hideTabs" v-model:activeKey="activeKey" @change="onTabsChange">
|
||||
<a-tab-pane tab="数据库属性" key="dbTable" forceRender>
|
||||
<DBAttributeTable
|
||||
ref="dbTable"
|
||||
:isUpdate="isUpdate"
|
||||
:tableName="oldTableName"
|
||||
:actionButton="actionButton"
|
||||
@added="onTableAdded"
|
||||
@removed="onTableRemoved"
|
||||
@dragged="onTableDragged"
|
||||
@inserted="onTableInserted"
|
||||
@syncDbType="onTableSyncDbType"
|
||||
@syncDbIsPersist="onTableSyncDbIsPersist"
|
||||
@syncDbIsNull="onTableSyncDbIsNull"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="页面属性" key="pageTable" forceRender>
|
||||
<PageAttributeTable ref="pageTable" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="校验字段" key="checkTable" forceRender>
|
||||
<CheckDictTable ref="checkTable" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="外键" key="fkTable" forceRender>
|
||||
<ForeignKeyTable ref="fkTable" :actionButton="actionButton" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="索引" key="idxTable" forceRender>
|
||||
<IndexTable ref="idxTable" :actionButton="actionButton" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="queryTable" forceRender>
|
||||
<template #tab>
|
||||
<span>
|
||||
个性查询配置
|
||||
<a-tooltip>
|
||||
<template #title>允许自定义,查询表单字段控件类型!</template>
|
||||
<Icon icon="bx:help-circle"></Icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<QueryTable ref="queryTable" @query="onTableQuery" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</a-spin>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="footer-area">
|
||||
<div class="rightArea">
|
||||
<a-button @click="onCancel">关闭</a-button>
|
||||
<a-button type="primary" :loading="confirmLoading" preIcon="ant-design:save" @click="onSubmit">保存</a-button>
|
||||
</div>
|
||||
<div class="leftArea">
|
||||
<div v-if="aiTestMode && !isUpdate">
|
||||
<a-select
|
||||
v-model:value="aiTestTable"
|
||||
placeholder="请选择测试的数据模型"
|
||||
:getPopupContainer="(n) => n?.parentElement"
|
||||
style="width: 300px; margin: 0 10px 0 0;text-align: left;"
|
||||
>
|
||||
<template v-for="(item, index) in aiTableList" :key="index">
|
||||
<a-select-option :value="item.name">{{ item.title + '(' + item.name + ')' }}</a-select-option>
|
||||
</template>
|
||||
</a-select>
|
||||
<a-button type="primary" ghost @click="initVirtualData">生成数据>></a-button>
|
||||
</div>
|
||||
<div v-if="isUpdate" class="positioning-area">
|
||||
<a-input v-model:value="positioning" placeholder="请输入字段名称或字段备注" allowClear @pressEnter="handlePositioning"></a-input>
|
||||
<a-button type="primary" ghost @click="handlePositioning">定位</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, computed, nextTick, provide, defineComponent, toRaw } from 'vue';
|
||||
import { useResizeObserver } from '@vueuse/core';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import DBAttributeTable from './tables/DBAttributeTable.vue';
|
||||
import CgformHeadForm from './CgformHeadForm.vue';
|
||||
import PageAttributeTable from './tables/PageAttributeTable.vue';
|
||||
import CheckDictTable from './tables/CheckDictTable.vue';
|
||||
import ForeignKeyTable from './tables/ForeignKeyTable.vue';
|
||||
import IndexTable from './tables/IndexTable.vue';
|
||||
import QueryTable from './tables/QueryTable.vue';
|
||||
import { CgformModal } from '../types';
|
||||
import { useInitialData, VALIDATE_FAILED, useTreeNeedFields } from '../cgform.data';
|
||||
import { formApi } from '../cgform.api';
|
||||
import { simpleDebounce } from '/@/utils/common/compUtils';
|
||||
import { useOnlineTest } from '../hooks/aitest/useOnlineTest';
|
||||
import { buildUUID } from '/@/utils/uuid';
|
||||
import { sleep } from '/@/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CgformModal',
|
||||
components: {
|
||||
BasicModal,
|
||||
CgformHeadForm,
|
||||
DBAttributeTable,
|
||||
PageAttributeTable,
|
||||
CheckDictTable,
|
||||
ForeignKeyTable,
|
||||
IndexTable,
|
||||
QueryTable,
|
||||
Icon,
|
||||
},
|
||||
emits: ['success', 'register'],
|
||||
props: {
|
||||
actionButton: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const { createMessage: $message } = useMessage();
|
||||
const modalRef = ref();
|
||||
const spinRef = ref();
|
||||
// 是否是更新模式
|
||||
const isUpdate = ref(false);
|
||||
// 编辑时存储的值
|
||||
let model: Recordable = {};
|
||||
const title = computed(() => (isUpdate.value ? '编辑' : '新增'));
|
||||
// 当前是否正在加载中
|
||||
const confirmLoading = ref(true);
|
||||
// 表格区域正在加载中
|
||||
const tableLoading = ref(false);
|
||||
// tabs当前活动的页面
|
||||
const activeKey = ref('dbTable');
|
||||
// 解决打开弹窗速度缓慢的问题
|
||||
const hideTabs = ref(true);
|
||||
// 标记是否已经初始化过(v-show模式下组件不会被销毁)
|
||||
let tabsInitialized = false;
|
||||
// table refs
|
||||
const tables: CgformModal.TablesRef = {
|
||||
dbTable: ref<CgformModal.DBAttributeTableType>(),
|
||||
pageTable: ref<CgformModal.PageAttributeTableType>(),
|
||||
checkTable: ref<CgformModal.CheckDictTableType>(),
|
||||
fkTable: ref<CgformModal.ForeignKeyTableType>(),
|
||||
idxTable: ref<CgformModal.IndexTableType>(),
|
||||
queryTable: ref<CgformModal.QueryTableType>(),
|
||||
};
|
||||
// 当前是否是全屏状态
|
||||
const fullScreenRef = computed(() => modalRef.value?.fullScreenRef ?? false);
|
||||
provide('tables', tables);
|
||||
provide('fullScreenRef', fullScreenRef);
|
||||
const positioning = ref('');
|
||||
const onlFormContentRef = ref<HTMLElement>();
|
||||
const onlFormContentFormRef = ref<HTMLElement>();
|
||||
/** `.onlForm-content-form` 当前高度(随布局/展开收起等变化) */
|
||||
const onlFormContentFormHeight = ref(0);
|
||||
const vxetableHeight = ref(0);
|
||||
provide('vxetableHeight', vxetableHeight);
|
||||
useResizeObserver(onlFormContentFormRef, (entries) => {
|
||||
const entry = entries[0];
|
||||
if (entry) {
|
||||
onlFormContentFormHeight.value = entry.contentRect.height;
|
||||
vxetableHeight.value = onlFormContentRef.value!.offsetHeight - onlFormContentFormHeight.value - 128;
|
||||
}
|
||||
});
|
||||
|
||||
// 头部表单子组件 ref
|
||||
const cgformHeadFormRef = ref<InstanceType<typeof CgformHeadForm>>();
|
||||
// 代理 formAction 供 ExtendConfigModal 和 useOnlineTest 使用
|
||||
const formAction = {
|
||||
getFieldsValue: (fields?: string[]) => cgformHeadFormRef.value?.getFieldsValue(fields) ?? {},
|
||||
setFieldsValue: (values: Recordable) => cgformHeadFormRef.value?.setFieldsValue(values),
|
||||
validateFields: (_fields?: string[]) => Promise.resolve(),
|
||||
resetFields: () => cgformHeadFormRef.value?.resetFields(),
|
||||
};
|
||||
// 表单赋值
|
||||
const [registerModal, { closeModal }] = useModalInner((data) => {
|
||||
isUpdate.value = data?.isUpdate ?? false;
|
||||
if (isUpdate.value) {
|
||||
edit(data?.record);
|
||||
} else {
|
||||
add();
|
||||
}
|
||||
});
|
||||
// 旧表名
|
||||
const oldTableName = ref('');
|
||||
// 立即同步所有 table(防抖版)
|
||||
const syncAllTableNowDebounce = simpleDebounce(() => syncAllTableNowPromise(), 150);
|
||||
// 临时数据ID,不提交到后台
|
||||
let fieldTempIds: string[] = [];
|
||||
// 是否显示附表字段
|
||||
let showSubTableStr = false;
|
||||
// 是否已添加树表单字段
|
||||
let treeFieldAdded = false;
|
||||
// 已添加的树表单字段ID
|
||||
let treeFieldIds: string[] = [];
|
||||
let interval: any = null;
|
||||
|
||||
// hook OnlineTest
|
||||
const { aiTestMode, aiTestTable, aiTableList, initVirtualData, tableJsonGetHelper, refreshCacheTableName } = useOnlineTest(
|
||||
{
|
||||
oldTableName,
|
||||
tables,
|
||||
},
|
||||
{
|
||||
initialAllShowItem,
|
||||
setAllTableData,
|
||||
},
|
||||
formAction
|
||||
);
|
||||
|
||||
function add() {
|
||||
edit({});
|
||||
}
|
||||
|
||||
async function edit(record) {
|
||||
confirmLoading.value = false;
|
||||
activeKey.value = 'dbTable';
|
||||
// 重置表单
|
||||
cgformHeadFormRef.value?.resetFields();
|
||||
model = Object.assign({}, record);
|
||||
initialAllShowItem(model);
|
||||
// 重置定位内容
|
||||
positioning.value = '';
|
||||
|
||||
// 便于 ai test data 复制
|
||||
tableJsonGetHelper(model);
|
||||
|
||||
cgformHeadFormRef.value?.initialExtConfigJson(model);
|
||||
cgformHeadFormRef.value?.setFieldsValue(model);
|
||||
oldTableName.value = model.tableName;
|
||||
// update-begin--author:liaozhiyang---date:20260330---for:【QQYUN-13610】解决online打开慢的问题
|
||||
// 解决打开弹窗速度缓慢的问题(第一次延迟显示,后续直接显示)
|
||||
if (tabsInitialized) {
|
||||
hideTabs.value = false;
|
||||
// v-show模式下组件不会被销毁,需要先清空所有表的旧数据
|
||||
clearAllTableData();
|
||||
} else {
|
||||
sleep(1, () => {
|
||||
hideTabs.value = false;
|
||||
tabsInitialized = true;
|
||||
});
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260330---for:【QQYUN-13610】解决online打开慢的问题
|
||||
// update模式,加载数据库中的数据
|
||||
if (isUpdate.value) {
|
||||
// update-begin--author:liaozhiyang---date:20260210---for:【QQYUN-13658】Jvxetable、vxetable按需加载
|
||||
await getRefPromise(tables.dbTable);
|
||||
// update-end--author:liaozhiyang---date:20260209---for:【QQYUN-13658】Jvxetable、vxetable按需加载
|
||||
tables.dbTable.value?.setDataSource([]);
|
||||
await loadFields(model.id);
|
||||
// 加载index的数据,由于默认不可见,所以可以分开加载,提升加载效率
|
||||
await loadIndexes(model.id);
|
||||
// 设置子表下拉选择
|
||||
getRefPromise(tables.pageTable).then(() => {
|
||||
tables.pageTable.value!.changePageType(model.tableType == 3, model.relationType);
|
||||
});
|
||||
} else {
|
||||
// 添加初始数据
|
||||
let { initialData, tempIds } = useInitialData();
|
||||
await setAllTableData(initialData, true);
|
||||
fieldTempIds = tempIds;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载表字段配置
|
||||
async function loadFields(headId) {
|
||||
tableLoading.value = true;
|
||||
try {
|
||||
let fields = await formApi.doQueryField(headId);
|
||||
// 便于 ai test data 复制
|
||||
console.log("online fields:", fields)
|
||||
tableLoading.value = false;
|
||||
await setAllTableData(fields);
|
||||
} finally {
|
||||
tableLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载表索引配置
|
||||
async function loadIndexes(headId) {
|
||||
let indexes = await formApi.doQueryIndexes(headId);
|
||||
// 便于 ai test data 复制
|
||||
console.log("online indexs:", indexes)
|
||||
// update-begin--author:liaozhiyang---date:20260210---for:【QQYUN-13658】Jvxetable、vxetable按需加载
|
||||
await getRefPromise(tables.idxTable);
|
||||
// update-end--author:liaozhiyang---date:20260209---for:【QQYUN-13658】Jvxetable、vxetable按需加载
|
||||
tables.idxTable.value!.setDataSource(indexes);
|
||||
}
|
||||
|
||||
function initialAllShowItem(model) {
|
||||
treeFieldAdded = model.isTree == 'Y';
|
||||
showSubTableStr = model.tableType === 2;
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20260330---for:【QQYUN-13610】解决online打开慢的问题
|
||||
// 清空所有表的数据(v-show模式下切换数据前调用,避免旧ID匹配报错)
|
||||
function clearAllTableData() {
|
||||
const { dbTable, pageTable, checkTable, fkTable, idxTable, queryTable } = tables;
|
||||
dbTable.value?.setDataSource([]);
|
||||
pageTable.value?.setDataSource([]);
|
||||
checkTable.value?.setDataSource([]);
|
||||
fkTable.value?.setDataSource([]);
|
||||
idxTable.value?.setDataSource([]);
|
||||
queryTable.value?.setDataSource([]);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260330---for:【QQYUN-13610】解决online打开慢的问题
|
||||
|
||||
// 设置除索引配置之外所有的JVxeTable的数据
|
||||
async function setAllTableData(data: Recordable[], insert?) {
|
||||
const { dbTable, pageTable, checkTable, fkTable, queryTable } = tables;
|
||||
// update-begin--author:liaozhiyang---date:20260210---for:【QQYUN-13658】Jvxetable、vxetable按需加载
|
||||
await getRefPromise(dbTable);
|
||||
// update-end--author:liaozhiyang---date:20260209---for:【QQYUN-13658】Jvxetable、vxetable按需加载
|
||||
dbTable.value!.setDataSource(data, insert);
|
||||
// 先加载第一个tab的数据,延时加载其他tab,可以使打开速度的视觉效果更好
|
||||
// update-begin--author:liaozhiyang---date:20260210---for:【QQYUN-13658】Jvxetable、vxetable按需加载
|
||||
setTimeout(async () => {
|
||||
await Promise.all([
|
||||
getRefPromise(pageTable),
|
||||
getRefPromise(checkTable),
|
||||
getRefPromise(fkTable),
|
||||
getRefPromise(queryTable),
|
||||
]);
|
||||
// update-end--author:liaozhiyang---date:20260209---for:【QQYUN-13658】Jvxetable、vxetable按需加载
|
||||
pageTable.value!.setDataSource(data, insert);
|
||||
checkTable.value!.setDataSource(data, insert);
|
||||
fkTable.value!.setDataSource(data, insert);
|
||||
queryTable.value!.setDataSource(data, insert);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
/** ATab切换事件 */
|
||||
function onTabsChange(activeKey) {
|
||||
// 当切换了选项卡的时候只同步修改当前所能看到的table
|
||||
if (['pageTable', 'checkTable', 'fkTable', 'idxTable', 'queryTable'].indexOf(activeKey) !== -1) {
|
||||
const dbTable = tables.dbTable;
|
||||
const table = tables[activeKey];
|
||||
dbTable.value!.tableRef!.resetScrollTop();
|
||||
// update-begin--author:liaozhiyang---date:20260316---for:【QQYUN-13751】jVxetable优化
|
||||
clearInterval(interval);
|
||||
interval = setTimeout(() => {
|
||||
table.value.syncTable(dbTable);
|
||||
}, 200);
|
||||
// update-end--author:liaozhiyang---date:20260316---for:【QQYUN-13751】jVxetable优化
|
||||
}
|
||||
}
|
||||
|
||||
// 表类型tableType字段change事件(由子组件 emit 触发,formModel 已在子组件内更新)
|
||||
function onTableTypeChange(value) {
|
||||
const relationType = cgformHeadFormRef.value?.formModel.relationType ?? 0;
|
||||
tables.pageTable.value!.changePageType(value == 3, relationType);
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20260317---for:【QQYUN-9441】online一对多加上关联记录和他表字段
|
||||
// 关联类型relationType字段change事件
|
||||
function onRelationTypeChange(value) {
|
||||
const tableType = cgformHeadFormRef.value?.formModel.tableType ?? 1;
|
||||
tables.pageTable.value!.changePageType(tableType == 3, value);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260317---for:【QQYUN-9441】online一对多加上关联记录和他表字段
|
||||
|
||||
// 是否树isTree字段change事件
|
||||
function onIsTreeChange(value) {
|
||||
value === 'Y' ? addTreeNeedField() : deleteTreeNeedField();
|
||||
}
|
||||
|
||||
/** 立即主动同步所有table */
|
||||
function syncAllTableNow() {
|
||||
syncAllTableNowDebounce();
|
||||
}
|
||||
|
||||
// 立即同步所有 table
|
||||
async function syncAllTableNowPromise() {
|
||||
let { dbTable, pageTable, checkTable, fkTable, queryTable } = tables;
|
||||
await pageTable.value!.syncTable(dbTable);
|
||||
await checkTable.value!.syncTable(dbTable);
|
||||
await fkTable.value!.syncTable(dbTable);
|
||||
await queryTable.value!.syncTable(dbTable);
|
||||
}
|
||||
|
||||
// update-begin--author:liaozhiyang---date:20260414---for:【QQYUN-15128】新增字段时系统字段永远在最下面
|
||||
const SYS_BUILT_IN_FIELDS = ['create_by', 'create_time', 'update_by', 'update_time', 'sys_org_code'];
|
||||
// update-end--author:liaozhiyang---date:20260414---for:【QQYUN-15128】新增字段时系统字段永远在最下面
|
||||
|
||||
/** 当新增了的时候应立即同步 */
|
||||
async function onTableAdded() {
|
||||
// update-begin--author:liaozhiyang---date:20260414---for:【QQYUN-15128】新增行时系统字段永远在最下面(系统字段被删除时退化为正常追加)
|
||||
const dbJVxeRef = tables.dbTable.value?.tableRef;
|
||||
if (dbJVxeRef) {
|
||||
const fullData = dbJVxeRef.getXTable().internalData.tableFullData;
|
||||
const sysIndex = fullData.findIndex((row) => SYS_BUILT_IN_FIELDS.includes(row.dbFieldName));
|
||||
const lastIndex = fullData.length - 1;
|
||||
// 末尾是新增行,且在系统字段后面,且本身不是系统字段
|
||||
if (sysIndex !== -1 && lastIndex > sysIndex && !SYS_BUILT_IN_FIELDS.includes(fullData[lastIndex]?.dbFieldName)) {
|
||||
const newRowData = { ...fullData[lastIndex] };
|
||||
// 在 dbTable 中把末尾新行移到第一个系统字段前面
|
||||
await dbJVxeRef.rowResort(lastIndex, sysIndex);
|
||||
// 对其他 tables 直接在系统字段前面插入,不走 syncAllTableNow 的追加逻辑
|
||||
const { pageTable, checkTable, fkTable, queryTable } = tables;
|
||||
for (const t of [pageTable, checkTable, fkTable, queryTable]) {
|
||||
const jvxeRef = t.value?.tableRef;
|
||||
if (!jvxeRef) continue;
|
||||
const tFullData = jvxeRef.getXTable().internalData.tableFullData;
|
||||
const tSysIndex = tFullData.findIndex((row) => SYS_BUILT_IN_FIELDS.includes(row.dbFieldName));
|
||||
if (tSysIndex !== -1) {
|
||||
jvxeRef.insertRows(newRowData, tSysIndex);
|
||||
} else {
|
||||
jvxeRef.addRows(newRowData);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260414---for:【QQYUN-15128】新增行时系统字段永远在最下面(系统字段被删除时退化为正常追加)
|
||||
syncAllTableNow();
|
||||
}
|
||||
|
||||
/** 当删除的时候也应立即同步 */
|
||||
function onTableRemoved() {
|
||||
syncAllTableNow();
|
||||
}
|
||||
|
||||
/** 当拖动后立即同步 */
|
||||
function onTableDragged(event) {
|
||||
let { oldIndex, newIndex } = event;
|
||||
syncAllOrderNumNow(oldIndex, newIndex);
|
||||
}
|
||||
|
||||
/** 当插入后立即同步 */
|
||||
async function onTableInserted(event) {
|
||||
let { insertIndex, row } = event;
|
||||
let { pageTable, checkTable, fkTable, queryTable } = tables;
|
||||
pageTable.value!.tableRef!.insertRows(row, insertIndex);
|
||||
checkTable.value!.tableRef!.insertRows(row, insertIndex);
|
||||
fkTable.value!.tableRef!.insertRows(row, insertIndex);
|
||||
queryTable.value!.tableRef!.insertRows(row, insertIndex);
|
||||
}
|
||||
|
||||
/** 立即同步所有的表的排序顺序 */
|
||||
function syncAllOrderNumNow(oldIndex: number, newIndex: number) {
|
||||
let { pageTable, checkTable, fkTable, queryTable } = tables;
|
||||
pageTable.value!.tableRef!.rowResort(oldIndex, newIndex);
|
||||
checkTable.value!.tableRef!.rowResort(oldIndex, newIndex);
|
||||
fkTable.value!.tableRef!.rowResort(oldIndex, newIndex);
|
||||
queryTable.value!.tableRef!.rowResort(oldIndex, newIndex);
|
||||
}
|
||||
|
||||
/** 当value变化时同步 date */
|
||||
function onTableSyncDbType(event) {
|
||||
tables.pageTable.value!.syncFieldShowType(event.row);
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240313---for:【QQYUN-8485】不同步数据库的字段则去掉对应查询勾选
|
||||
/** 当dbIsPersist(同步数据库) value变化时同步 查询去掉勾选 */
|
||||
function onTableSyncDbIsPersist(event) {
|
||||
tables.pageTable.value!.syncIsQuery(event.row);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240313---for:【QQYUN-8485】不同步数据库的字段则去掉对应查询勾选
|
||||
// update-begin--author:liaozhiyang---date:20240313---for:【QQYUN-8485】数据库不允许为空,校验默认相应勾上
|
||||
/** 当dbIsNull(不允许空值) value变化时同步 校验必填 */
|
||||
function onTableSyncDbIsNull(event) {
|
||||
tables.checkTable.value!.syncFieldMustInput(event.row);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240313---for:【QQYUN-8485】数据库不允许为空,校验默认相应勾上
|
||||
|
||||
function onTableQuery(id) {
|
||||
tables.pageTable.value!.enableQuery(id);
|
||||
}
|
||||
|
||||
/** 添加树字段 */
|
||||
function addTreeNeedField() {
|
||||
if (!treeFieldAdded) {
|
||||
let { dbTable, pageTable, checkTable } = tables;
|
||||
let treeFields = useTreeNeedFields();
|
||||
treeFields = treeFields.filter((item: any) => {
|
||||
let nameList = dbTable.value!.tableRef!.getTableData().map((o) => o.dbFieldName);
|
||||
return !nameList.includes(item.dbFieldName);
|
||||
});
|
||||
treeFieldIds = [];
|
||||
treeFields.forEach((newData: any) => {
|
||||
let uuidTemp = buildUUID() + '__tempId';
|
||||
treeFieldIds.push(uuidTemp);
|
||||
newData.id = uuidTemp;
|
||||
});
|
||||
dbTable.value!.tableRef!.addRows(treeFields, { setActive: false });
|
||||
pageTable.value!.tableRef!.addRows(treeFields, { setActive: false });
|
||||
checkTable.value!.tableRef!.addRows(treeFields, { setActive: false });
|
||||
nextTick(() => syncAllTableNow());
|
||||
treeFieldAdded = true;
|
||||
}
|
||||
nextTick(() => {
|
||||
if (cgformHeadFormRef.value) {
|
||||
cgformHeadFormRef.value.formModel.treeIdField = 'has_child';
|
||||
cgformHeadFormRef.value.formModel.treeParentIdField = 'pid';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除树字段 */
|
||||
function deleteTreeNeedField() {
|
||||
if (treeFieldIds && treeFieldIds.length > 0) {
|
||||
let { dbTable } = tables;
|
||||
dbTable.value!.tableDeleteLines(treeFieldIds);
|
||||
treeFieldIds = [];
|
||||
treeFieldAdded = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 触发所有表单验证
|
||||
function validateAll() {
|
||||
let options = {};
|
||||
return new Promise((resolve, reject) => {
|
||||
// 验证主表表单
|
||||
cgformHeadFormRef.value!.validate().then(
|
||||
(values) => resolve({ values }),
|
||||
(errMsg) => {
|
||||
if (errMsg) $message.warning(errMsg);
|
||||
reject(VALIDATE_FAILED);
|
||||
}
|
||||
);
|
||||
})
|
||||
.then((result) => {
|
||||
Object.assign(options, result);
|
||||
return validateTableFields();
|
||||
})
|
||||
.then((allTableData) => {
|
||||
Object.assign(options, allTableData);
|
||||
let formData = classifyIntoFormData(options);
|
||||
return validateForeignKey(formData);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e === VALIDATE_FAILED || e?.code === VALIDATE_FAILED) {
|
||||
// 表单校验失败时消息已在 validate() 内提示,此处不重复
|
||||
} else {
|
||||
// update-begin--author:liaozhiyang---date:20231226---for:【QQYUN-7503】附表配置多个外键,保存失败没提示
|
||||
e?.msg ? $message.warning(e.msg) : console.error(e);
|
||||
// update-end--author:liaozhiyang---date:20231226---for:【QQYUN-7503】附表配置多个外键,保存失败没提示
|
||||
}
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/** 验证并获取所有表的数据 */
|
||||
function validateTableFields() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let tableKeys = Object.keys(tables);
|
||||
let allTableData: any = {};
|
||||
for (let i = 0; i < tableKeys.length; i++) {
|
||||
let key = tableKeys[i];
|
||||
let table = tables[key];
|
||||
try {
|
||||
allTableData[key] = await table.value!.validateData(key);
|
||||
} catch (e: any) {
|
||||
if (e.code === VALIDATE_FAILED) {
|
||||
// 未通过就跳转到相应的tab选项卡
|
||||
activeKey.value = e.activeKey;
|
||||
} else {
|
||||
console.error(e);
|
||||
}
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
resolve(allTableData);
|
||||
});
|
||||
}
|
||||
|
||||
/** 将所有表的数据整理整合成后台识别的formData */
|
||||
function classifyIntoFormData(options) {
|
||||
// 整理数据
|
||||
let formData = {
|
||||
head: {} as Recordable,
|
||||
fields: [] as any[],
|
||||
indexs: [] as any[],
|
||||
deleteFieldIds: [] as any[],
|
||||
deleteIndexIds: [] as any[],
|
||||
};
|
||||
formData.head = Object.assign(model, options.values);
|
||||
// update-begin--author:liaozhiyang---date:20260401---for:【QQYUN-14949】online配置中尽可能多的显示vxetable字段
|
||||
formData.head.formCategory = 'temp';
|
||||
formData.head.idType = 'UUID';
|
||||
// update-end--author:liaozhiyang---date:20260401---for:【QQYUN-14949】online配置中尽可能多的显示vxetable字段
|
||||
// 整理online表单扩展JSON
|
||||
const extConfigJson = { ...cgformHeadFormRef.value?.getExtConfigJson() };
|
||||
formData.head.isDesForm = extConfigJson.isDesForm;
|
||||
formData.head.desFormCode = extConfigJson.desFormCode;
|
||||
delete extConfigJson.isDesForm;
|
||||
delete extConfigJson.desFormCode;
|
||||
formData.head.extConfigJson = JSON.stringify(extConfigJson);
|
||||
// 整理 fields
|
||||
options.dbTable.tableData.forEach((item, index) => {
|
||||
// ID 以 dbTable 的 ID 为准
|
||||
let rowId = item.id;
|
||||
let fields = Object.assign({}, item);
|
||||
|
||||
let pageTable = options.pageTable.tableData[index];
|
||||
fields = Object.assign(pageTable, fields);
|
||||
|
||||
let checkTable = options.checkTable.tableData[index];
|
||||
fields = Object.assign(checkTable, fields);
|
||||
|
||||
let fkTable = options.fkTable.tableData[index];
|
||||
fields = Object.assign(fkTable, fields);
|
||||
|
||||
let queryTable = options.queryTable.tableData[index];
|
||||
fields = Object.assign(queryTable, fields);
|
||||
|
||||
// 如果 dbTable 没有返回id,则代表是新增的数据
|
||||
if (rowId == null || rowId === '') {
|
||||
delete fields.id;
|
||||
} else {
|
||||
fields.id = rowId;
|
||||
}
|
||||
// 去掉临时ID
|
||||
let tempIds = ([] as string[]).concat(fieldTempIds, treeFieldIds);
|
||||
if (tempIds.includes(fields.id)) {
|
||||
delete fields.id;
|
||||
}
|
||||
formData.fields.push(fields);
|
||||
});
|
||||
formData.deleteFieldIds = options.dbTable.deleteIds;
|
||||
// 整理 index
|
||||
formData.indexs = options.idxTable.tableData;
|
||||
formData.deleteIndexIds = options.idxTable.deleteIds;
|
||||
return formData;
|
||||
}
|
||||
|
||||
/** 外键配置校验 只能配置一个 */
|
||||
function validateForeignKey(formData) {
|
||||
// 1.配置两个 校验
|
||||
// 2.配置一个后,保存,再配置新的 删除老的 校验
|
||||
// 3.配置一个后,保存,修改当前为新的 校验
|
||||
return new Promise((resolve, reject) => {
|
||||
let fields = formData.fields;
|
||||
let saved = true;
|
||||
if (fields && fields.length > 0) {
|
||||
let hasForeignKey = 0;
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
if (fields[i].mainField || fields[i].mainTable) {
|
||||
hasForeignKey += 1;
|
||||
}
|
||||
if (hasForeignKey > 1) {
|
||||
saved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (saved) {
|
||||
resolve(formData);
|
||||
} else {
|
||||
reject({
|
||||
code: -1,
|
||||
msg: '外键只允许配置一个!',
|
||||
error: VALIDATE_FAILED,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 表单提交事件
|
||||
function onSubmit() {
|
||||
confirmLoading.value = true;
|
||||
validateAll()
|
||||
.then(
|
||||
async (formData: any) => {
|
||||
// 表字段转小写
|
||||
if (formData.fields && formData.fields.length > 0) {
|
||||
for (let field of formData.fields) {
|
||||
field.dbFieldName = field.dbFieldName.toLowerCase().trim();
|
||||
}
|
||||
}
|
||||
if (formData.head?.tableName) {
|
||||
formData.head.tableName = formData.head.tableName.toLowerCase().trim();
|
||||
}
|
||||
// 发起请求
|
||||
await formApi.doSaveOrUpdate(formData, isUpdate.value);
|
||||
refreshCacheTableName(oldTableName.value, formData.head['tableName']);
|
||||
emit('success');
|
||||
// 解决关闭弹窗时会闪一下的问题,因为同时加载多个JVxeTable造成的卡顿影响了弹窗关闭效果
|
||||
sleep(1, () => onCancel());
|
||||
},
|
||||
(e) => {
|
||||
console.error(e);
|
||||
}
|
||||
)
|
||||
.finally(() => {
|
||||
confirmLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
//update-begin-author:taoyan date:2022-8-15 for: VUEN-1891 online表单编辑时 修改了扩展配置能否 确认即保存,不用再点整个表单得确定
|
||||
async function onExtConfigSaved(values) {
|
||||
if (isUpdate.value) {
|
||||
const params = {
|
||||
id: model.id,
|
||||
extConfigJson: JSON.stringify(toRaw(values)),
|
||||
};
|
||||
await formApi.editHead(params);
|
||||
emit('success');
|
||||
}
|
||||
}
|
||||
//update-end-author:taoyan date:2022-8-15 for: VUEN-1891 online表单编辑时 修改了扩展配置能否 确认即保存,不用再点整个表单得确定
|
||||
|
||||
function onCancel() {
|
||||
hideTabs.value = true;
|
||||
// 解决关闭弹窗时会闪一下的问题,因为同时加载多个JVxeTable造成的卡顿影响了弹窗关闭效果
|
||||
sleep(1, () => closeModal());
|
||||
}
|
||||
/**
|
||||
* 2024-07-17
|
||||
* liaozhiyang
|
||||
* 【TV360X-829】根据字典名称和字段备注快速定位到行
|
||||
* */
|
||||
const handlePositioning = () => {
|
||||
const val = positioning.value.trim();
|
||||
if (val.length) {
|
||||
const jVxe_instance = tables[activeKey.value].value.tableRef;
|
||||
const vxe_instance = jVxe_instance.getXTable();
|
||||
const fullData = vxe_instance.getTableData().fullData;
|
||||
// 先精确,再模糊
|
||||
const preciseIndex = fullData.findIndex((item) => val === item.dbFieldName || val === item.dbFieldTxt);
|
||||
let index = -1;
|
||||
if (preciseIndex == -1) {
|
||||
// 模糊
|
||||
const dimIndex = fullData.findIndex((item) => item.dbFieldName.includes(positioning.value) || item.dbFieldTxt.includes(positioning.value));
|
||||
index = dimIndex;
|
||||
} else {
|
||||
index = preciseIndex;
|
||||
}
|
||||
if(index != -1) {
|
||||
const row = fullData[index];
|
||||
vxe_instance.scrollToRow(row).then(() => {
|
||||
const { refTableBody } = vxe_instance.getRefMaps();
|
||||
const tableBody = refTableBody.value;
|
||||
const bodyElem = tableBody ? tableBody.$el : null;
|
||||
if (bodyElem) {
|
||||
const trElem = bodyElem.querySelector(`[rowid="${vxe_instance.getRowid(row)}"]`);
|
||||
if (trElem) {
|
||||
trElem.classList.add('customHighlight');
|
||||
setTimeout(() => {
|
||||
trElem?.classList.remove('customHighlight');
|
||||
}, 1e3);
|
||||
}
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20260330---for:【QQYUN-15058】解决字段定位在小屏幕上看不见的问题
|
||||
// 将弹窗竖向滚动条滚动到最底部
|
||||
const spinEl = spinRef.value?.$el || spinRef.value;
|
||||
const scrollContainer = spinEl?.closest('.scrollbar__wrap');
|
||||
if (scrollContainer) {
|
||||
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20260330---for:【QQYUN-15058】解决字段定位在小屏幕上看不见的问题
|
||||
});
|
||||
} else {
|
||||
$message.warning('没搜到相关字段名称或字段备注~');
|
||||
}
|
||||
} else {
|
||||
$message.warning('请输入字段名称或字段备注~');
|
||||
}
|
||||
};
|
||||
function getRefPromise(componentRef) {
|
||||
return new Promise((resolve) => {
|
||||
function next() {
|
||||
let ref = componentRef.value;
|
||||
if (ref?.tableRef?.getXTable) {
|
||||
resolve(ref);
|
||||
} else {
|
||||
requestAnimationFrame(next);
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
return {
|
||||
...tables,
|
||||
modalRef,
|
||||
onlFormContentRef,
|
||||
onlFormContentFormRef,
|
||||
onlFormContentFormHeight,
|
||||
spinRef,
|
||||
title,
|
||||
confirmLoading,
|
||||
tableLoading,
|
||||
activeKey,
|
||||
onCancel,
|
||||
formAction,
|
||||
hideTabs,
|
||||
onSubmit,
|
||||
onTabsChange,
|
||||
onTableAdded,
|
||||
onTableRemoved,
|
||||
onTableDragged,
|
||||
onTableInserted,
|
||||
onTableSyncDbType,
|
||||
onTableQuery,
|
||||
onExtConfigSaved,
|
||||
registerModal,
|
||||
// hook OnlineTest
|
||||
aiTestMode,
|
||||
aiTestTable,
|
||||
aiTableList,
|
||||
initVirtualData,
|
||||
onTableSyncDbIsPersist,
|
||||
onTableSyncDbIsNull,
|
||||
isUpdate,
|
||||
positioning,
|
||||
handlePositioning,
|
||||
oldTableName,
|
||||
onIsTreeChange,
|
||||
// 头部表单子组件
|
||||
cgformHeadFormRef,
|
||||
onTableTypeChange,
|
||||
onRelationTypeChange,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.onlForm-content {
|
||||
height: 100%;
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240717---for:【TV360X-829】根据字典名称和字段备注快速定位到行
|
||||
.footer-area {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row-reverse;
|
||||
.leftArea {
|
||||
display: flex;
|
||||
> * {
|
||||
&:not(:first-child) {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.positioning-area {
|
||||
width: 280px;
|
||||
display: flex;
|
||||
> :first-child {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
:deep(.vxe-table) {
|
||||
.vxe-body--row.customHighlight {
|
||||
background-color: var(--vxe-ui-table-row-hover-background-color);
|
||||
}
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240717---for:【TV360X-829】根据字典名称和字段备注快速定位到行
|
||||
</style>
|
||||
<style>
|
||||
.onlForm-config-modal {
|
||||
.scroll-container .scrollbar__wrap {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" :width="1200" :defaultFullscreen="false" :canFullscreen="false">
|
||||
<template #title> <info-circle-two-tone /> 代码生成结果 </template>
|
||||
<div :style="divStyle">
|
||||
<p>
|
||||
<template v-for="item in codeList"> {{ item }}<br /> </template>
|
||||
</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button @click="handleClose">关闭</a-button>
|
||||
<a-button type="primary" ghost @click="handleView">在线预览</a-button>
|
||||
<a-button type="primary" @click="onDownloadGenerateCode" :loading="loading">下载到本地</a-button>
|
||||
</template>
|
||||
</BasicModal>
|
||||
<code-file-view-modal @register="registerCodeViewModal" @download="onDownloadGenerateCode" @close="handleClose"></code-file-view-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* online代码生成后弹出的modal 文件列表
|
||||
*/
|
||||
import { ref, reactive, computed, nextTick, defineComponent } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { BasicForm, useForm } from '/@/components/Form';
|
||||
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
|
||||
import { InfoCircleTwoTone } from '@ant-design/icons-vue';
|
||||
import CodeFileViewModal from './CodeFileViewModal.vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
|
||||
export default {
|
||||
name: 'CodeFileListModal',
|
||||
components: {
|
||||
BasicModal,
|
||||
InfoCircleTwoTone,
|
||||
CodeFileViewModal,
|
||||
},
|
||||
emits: ['register'],
|
||||
setup() {
|
||||
const { createMessage: $message } = useMessage();
|
||||
const codeList = ref([]);
|
||||
const height = window.innerHeight - 150;
|
||||
const divStyle = reactive({
|
||||
overflowY: 'auto',
|
||||
maxHeight: height + 'px',
|
||||
});
|
||||
const loading = ref(false);
|
||||
const tableName = ref('')
|
||||
const pathKey = ref('')
|
||||
|
||||
const [registerModal, { closeModal }] = useModalInner(async (data) => {
|
||||
codeList.value = data.codeList;
|
||||
tableName.value = data.tableName;
|
||||
pathKey.value = data.pathKey;
|
||||
});
|
||||
|
||||
function handleClose() {
|
||||
closeModal();
|
||||
}
|
||||
function onDownloadGenerateCode() {
|
||||
//update-begin-author:taoyan date:2022-6-27 for: VUEN-1433【vue3】一对多代码生成,点击下载失败
|
||||
let codeFileList = codeList.value;
|
||||
if (!codeFileList || codeFileList.length == 0) {
|
||||
$message.warning('无代码!');
|
||||
return;
|
||||
}
|
||||
let temp = codeFileList.join(',');
|
||||
//console.log(temp);
|
||||
//update-end-author:taoyan date:2022-6-27 for: VUEN-1433【vue3】一对多代码生成,点击下载失败
|
||||
return defHttp
|
||||
.post(
|
||||
{
|
||||
url: '/online/cgform/api/downGenerateCode',
|
||||
params: {
|
||||
fileList: encodeURI(temp),
|
||||
pathKey: pathKey.value
|
||||
},
|
||||
responseType: 'blob',
|
||||
},
|
||||
{ isTransformResponse: false }
|
||||
)
|
||||
.then((data) => {
|
||||
if (!data || data.size == 0) {
|
||||
$message.warning('导出代码失败!');
|
||||
return;
|
||||
}
|
||||
let fileName = '导到生成代码_' + tableName.value + '_' + new Date().getTime() + '.zip';
|
||||
downloadByData(data, fileName, 'application/zip');
|
||||
});
|
||||
}
|
||||
|
||||
const [registerCodeViewModal, { openModal }] = useModal();
|
||||
function handleView() {
|
||||
let temp = codeList.value;
|
||||
openModal(true, {
|
||||
codeList: temp,
|
||||
pathKey: pathKey.value
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
registerModal,
|
||||
registerCodeViewModal,
|
||||
divStyle,
|
||||
codeList,
|
||||
onDownloadGenerateCode,
|
||||
handleClose,
|
||||
handleView,
|
||||
loading,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<BasicModal :height="modalHeight" @register="registerModal" okText="" cancelText="关闭" :width="1200" :defaultFullscreen="true" :canFullscreen="false" @ok="onDownloadGenerateCode" :wrapClassName="prefixCls">
|
||||
<template #title> <info-circle-two-tone /> 代码在线预览 </template>
|
||||
<div>
|
||||
<a-row class="code-gen">
|
||||
<div class="left" style="border-right: 1px solid #eee" :style="{width:`calc(${defaultLeftWidthRate}% - 2px)`}">
|
||||
<div :style="{ height: height + 'px', overflowY: 'auto' }">
|
||||
<a-directory-tree v-if="treeData.length" :defaultExpandAll="true" :tree-data="treeData" @select="showCodeContent"> </a-directory-tree>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resize"/>
|
||||
<div class="right" :style="{width:`calc(100% - ${defaultLeftWidthRate}% - 2px)`}">
|
||||
<JCodeEditor
|
||||
v-if="activeCodeContent"
|
||||
v-model:value="activeCodeContent"
|
||||
theme="idea"
|
||||
:language="language"
|
||||
:fullScreen="false"
|
||||
:lineNumbers="true"
|
||||
:height="height + 'px'"
|
||||
:disabled="true"
|
||||
:language-change="true">
|
||||
</JCodeEditor>
|
||||
<a-empty v-else style="margin-top: 50px" description="请选择左侧文件,显示详细代码" />
|
||||
</div>
|
||||
</a-row>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button @click="handleClose">关闭</a-button>
|
||||
<a-button type="primary" @click="onDownloadGenerateCode">下载到本地</a-button>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
/**
|
||||
* online代码生成后支持在线预览
|
||||
*/
|
||||
import {ref, defineComponent, reactive, onMounted} from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { InfoCircleTwoTone } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { JCodeEditor } from '/@/components/Form';
|
||||
import 'codemirror/theme/idea.css';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CodeFileViewModal',
|
||||
components: {
|
||||
BasicModal,
|
||||
InfoCircleTwoTone,
|
||||
JCodeEditor,
|
||||
},
|
||||
emits: ['download', 'register', 'close'],
|
||||
setup(_p, { emit }) {
|
||||
const codeList = ref([]);
|
||||
const pathKey = ref('');
|
||||
const treeData = ref<any[]>([]);
|
||||
const expandStatus = ref(false);
|
||||
const height = window.innerHeight - 142;
|
||||
const language = ref('java');
|
||||
const activeCodeContent = ref('');
|
||||
let codeMap = reactive({});
|
||||
|
||||
const [registerModal, { closeModal }] = useModalInner(async (data) => {
|
||||
codeMap = reactive({});
|
||||
activeCodeContent.value = '';
|
||||
|
||||
codeList.value = data.codeList;
|
||||
pathKey.value = data.pathKey;
|
||||
getTreeData();
|
||||
dragDiv();
|
||||
expandStatus.value = true;
|
||||
});
|
||||
const { prefixCls } = useDesign('online-codeFileViewModal');
|
||||
|
||||
function getTreeData() {
|
||||
let list = getPlainList();
|
||||
let root = list[0];
|
||||
assembleTree(root, list);
|
||||
let treeList: any[] = [];
|
||||
const getFinalTreeData = function (root) {
|
||||
if (root.children) {
|
||||
let children = root.children;
|
||||
if (children.length == 1) {
|
||||
getFinalTreeData(children[0]);
|
||||
} else if (children.length > 1) {
|
||||
treeList.push(root);
|
||||
}
|
||||
}
|
||||
};
|
||||
getFinalTreeData(root);
|
||||
console.log(123, treeList)
|
||||
treeData.value = treeList;
|
||||
setTimeout(()=>{
|
||||
loadFirstFileContent(root)
|
||||
}, 300)
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认加载第一个文件
|
||||
* @param root
|
||||
*/
|
||||
async function loadFirstFileContent(root){
|
||||
const getFirstFile = function(temp){
|
||||
if(temp.isLeaf === true){
|
||||
return temp;
|
||||
}else{
|
||||
if (temp.children) {
|
||||
return getFirstFile(temp.children[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
let node = getFirstFile(root);
|
||||
if(node && node.isLeaf === true){
|
||||
let path = node.path;
|
||||
if (!codeMap[path]) {
|
||||
await loadCode(path);
|
||||
}
|
||||
language.value = getCodeLanguage(path);
|
||||
activeCodeContent.value = codeMap[path];
|
||||
}
|
||||
}
|
||||
|
||||
function assembleTree(root, list) {
|
||||
for (let item of list) {
|
||||
if (root.key == item.pid) {
|
||||
let children = root.children;
|
||||
if (!children) {
|
||||
root.children = [];
|
||||
}
|
||||
root.children.push(item);
|
||||
assembleTree(item, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getAbsolutePath(arr, index){
|
||||
let i=0;
|
||||
let str = ''
|
||||
while(i<=index){
|
||||
str+=arr[i];
|
||||
i++;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function getPlainList() {
|
||||
// title key pid
|
||||
let list: any[] = [];
|
||||
let list2: any[] = [];
|
||||
let arr: any[] = codeList.value;
|
||||
for (let item of arr) {
|
||||
let temp = item.replace(new RegExp('\\\\', 'g'), '/').replace('生成成功:', '').trim();
|
||||
if (temp) {
|
||||
let arr2 = temp.split('/');
|
||||
for (let i = 0; i < arr2.length; i++) {
|
||||
let a = arr2[i];
|
||||
let id = getAbsolutePath(arr2, i)
|
||||
// let str = getAbsolutePath(arr2, i)
|
||||
if (a) {
|
||||
let item = {
|
||||
title: a,
|
||||
key: id,
|
||||
};
|
||||
if (a == 0) {
|
||||
} else {
|
||||
let lastKey = getAbsolutePath(arr2, i-1)
|
||||
//arr2[i - 1] + (i - 1);
|
||||
if (lastKey) {
|
||||
item['pid'] = lastKey;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
if (i == arr2.length - 1) {
|
||||
//最后一个元素
|
||||
item['isLeaf'] = true;
|
||||
item['path'] = temp;
|
||||
}
|
||||
if (list2.indexOf(id) < 0 || i == arr2.length - 1) {
|
||||
list.push(item);
|
||||
list2.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
closeModal();
|
||||
emit('close')
|
||||
}
|
||||
function onDownloadGenerateCode() {
|
||||
emit('download');
|
||||
}
|
||||
|
||||
function getCodeLanguage(path) {
|
||||
if (path.endsWith('xml')) {
|
||||
return 'application/xml';
|
||||
}
|
||||
if (path.endsWith('sql')) {
|
||||
return 'text/x-sql';
|
||||
}
|
||||
if (path.endsWith('vue')) {
|
||||
return 'text/x-vue';
|
||||
}
|
||||
if (path.endsWith('ts')) {
|
||||
return 'text/typescript';
|
||||
} else {
|
||||
return 'text/x-java';
|
||||
}
|
||||
}
|
||||
|
||||
async function showCodeContent(_selectedKeys, e) {
|
||||
let node = e.node.dataRef;
|
||||
if (node.isLeaf) {
|
||||
let path = node.path;
|
||||
if (!codeMap[path]) {
|
||||
await loadCode(path);
|
||||
}
|
||||
language.value = getCodeLanguage(path);
|
||||
activeCodeContent.value = codeMap[path];
|
||||
}
|
||||
}
|
||||
function loadCode(path) {
|
||||
return new Promise((resolve) => {
|
||||
let params = {
|
||||
path: encodeURI(path),
|
||||
pathKey: pathKey.value
|
||||
};
|
||||
defHttp.get({ url: '/online/cgform/api/codeView', params }, { isTransformResponse: false }).then((data) => {
|
||||
if (!data || data.size === 0) {
|
||||
message.warning('文件下载失败');
|
||||
return;
|
||||
}else{
|
||||
if (data.message) {
|
||||
message.warning(data.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let blob = new Blob([data]);
|
||||
let reader = new FileReader();
|
||||
reader.readAsText(blob, 'utf8');
|
||||
reader.onload = function () {
|
||||
let content = this.result;
|
||||
codeMap[path] = content;
|
||||
resolve(1);
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//update-begin---author:wangshuai---date:2025-08-25---for:【QQYUN-13519】代码预览这个允许左右拖动---
|
||||
//距离左侧的宽度
|
||||
const defaultLeftWidthRate = ref(24)
|
||||
//左侧显示最低半分比
|
||||
const leftMin = ref<number>(14);
|
||||
//右侧显示最低半分比
|
||||
const rightMin = ref<number>(30);
|
||||
|
||||
/**
|
||||
* div拖拽事件
|
||||
*
|
||||
*/
|
||||
function dragDiv(){
|
||||
let resize:any = document.getElementsByClassName('resize');
|
||||
let box:any = document.getElementsByClassName('code-gen');
|
||||
let left:any = document.getElementsByClassName('left');
|
||||
for (let i = 0; i < resize.length; i++) {
|
||||
// 鼠标按下事件
|
||||
resize[i].onmousedown = function (e: any) {
|
||||
//整个div的宽度
|
||||
let boxWidth = box[i].offsetWidth;
|
||||
//左侧的宽度
|
||||
let leftWidth = left[i].offsetWidth;
|
||||
// 开始位置
|
||||
let startX = e.clientX;
|
||||
// 鼠标拖动事件
|
||||
document.onmousemove = function (e: any) {
|
||||
// 结束位置
|
||||
let endX = e.clientX;
|
||||
// 得到鼠标拖动的宽高距离:取绝对值
|
||||
let distX = Math.abs(endX - startX);
|
||||
// 向右拖拽
|
||||
if (endX > startX) {
|
||||
let moveLate = parseFloat(((leftWidth + distX) / boxWidth * 100).toFixed(4));
|
||||
let right = parseFloat('100') - moveLate;
|
||||
// 向右大于右侧的最小值才允许拖拽
|
||||
if (right > rightMin.value) {
|
||||
defaultLeftWidthRate.value = moveLate;
|
||||
}
|
||||
}
|
||||
// 向左拖拽
|
||||
if (endX < startX) {
|
||||
let moveLate = parseFloat(((leftWidth - distX) / boxWidth * 100).toFixed(4));
|
||||
// 向左大于左侧的最小值才允许拖拽
|
||||
if (moveLate > leftMin.value) {
|
||||
defaultLeftWidthRate.value = moveLate;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 鼠标松开事件
|
||||
document.onmouseup = function (e: any) {
|
||||
document.onmousemove = null;
|
||||
document.onmouseup = null;
|
||||
//当你不在需要继续获得鼠标消息就要应该调用ReleaseCapture()释放掉
|
||||
resize[i].releaseCapture && resize[i].releaseCapture();
|
||||
}
|
||||
//该函数在属于当前线程的指定窗口里设置鼠标捕获
|
||||
resize[i].setCapture && resize[i].setCapture();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
//update-end---author:wangshuai---date:2025-08-25---for:【QQYUN-13519】代码预览这个允许左右拖动---
|
||||
|
||||
return {
|
||||
registerModal,
|
||||
codeList,
|
||||
onDownloadGenerateCode,
|
||||
handleClose,
|
||||
treeData,
|
||||
showCodeContent,
|
||||
activeCodeContent,
|
||||
expandStatus,
|
||||
height,
|
||||
language,
|
||||
prefixCls,
|
||||
modalHeight:1000,
|
||||
defaultLeftWidthRate
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
// update-begin--author:liaozhiyang---date:20240617---for:【TV360X-227】代码生成弹窗在线预览样式优化
|
||||
@prefix-cls: ~'@{namespace}-online-codeFileViewModal';
|
||||
.@{prefix-cls} {
|
||||
.scrollbar__wrap {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.ant-modal-footer {
|
||||
padding-top: 32px;
|
||||
}
|
||||
//update-begin---author:wangshuai---date:2025-08-25---for:【QQYUN-13519】代码预览这个允许左右拖动---
|
||||
.code-gen .left {
|
||||
position: relative;
|
||||
width: calc(24% - 2px);
|
||||
height: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.code-gen .resize {
|
||||
position: relative;
|
||||
width: 4px;
|
||||
color: #f7f7f7;
|
||||
background: #f7f7f7;
|
||||
cursor: w-resize;
|
||||
}
|
||||
|
||||
.code-gen .right {
|
||||
position: relative;
|
||||
width: calc(76% - 2px);
|
||||
height: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.ant-tree-node-content-wrapper{
|
||||
display: flex;
|
||||
}
|
||||
//update-end---author:wangshuai---date:2025-08-25---for:【QQYUN-13519】代码预览这个允许左右拖动---
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240617---for:【TV360X-227】代码生成弹窗在线预览样式优化
|
||||
</style>
|
||||
@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
@register="registerModal"
|
||||
:title="title"
|
||||
:width="modalWidth"
|
||||
:confirmLoading="confirmLoading"
|
||||
okText="开始生成"
|
||||
cancelText="取消"
|
||||
@ok="onSubmit"
|
||||
@cancel="onCancel"
|
||||
:wrapClassName="wrapClassName"
|
||||
>
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<BasicForm @register="registerForm">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240612---for:【TV360X-1057】代码生成页面代码鼠标移入给说明 -->
|
||||
<template #pageCode="{ model, field }">
|
||||
<a-radio-group v-model:value="model[field]">
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>深度封装表单,用户只需定义字段json即可渲染表单,优点简单便捷,缺点扩展有难度</span>
|
||||
</template>
|
||||
<a-radio value="vue3">封装表单(BasicForm)</a-radio>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>antd的原生表单,所有字段都需要硬编码,缺点编码繁琐,优点扩展容易</span>
|
||||
</template>
|
||||
<a-radio value="vue3Native" v-if="!(model.jspMode == 'innerTable' || model.jspMode == 'tab')">原生表单(a-form)</a-radio>
|
||||
</a-tooltip>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
<!-- update-end--author:liaozhiyang---date:20240612---for:【TV360X-1057】代码生成页面代码鼠标移入给说明 -->
|
||||
</BasicForm>
|
||||
<a-card v-if="showSubTable" title="子表信息" size="small">
|
||||
<JVxeTable ref="subTableRef" rowNumber :maxHeight="580" v-bind="subTable" />
|
||||
</a-card>
|
||||
</a-spin>
|
||||
</BasicModal>
|
||||
<FileSelectModal @register="registerFileSelectModal" @select="onFileSelect" />
|
||||
<!-- 生成代码文件后弹窗显示 -->
|
||||
<code-file-list-modal @register="registerCodeFileListModal"></code-file-list-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, reactive, computed, nextTick, defineComponent,h } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
// TODO import { DisabledAuthFilterMixin } from '@/mixins/DisabledAuthFilterMixin'
|
||||
import { BasicForm, useForm } from '/@/components/Form';
|
||||
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
|
||||
import { JVxeTypes, JVxeColumn, JVxeTableInstance } from '/@/components/jeecg/JVxeTable/types';
|
||||
import { useCodeGeneratorFormSchemas } from '../hooks/useSchemas';
|
||||
import { underLine2CamelCase } from '/@/utils/common/compUtils';
|
||||
import CodeFileListModal from './CodeFileListModal.vue'
|
||||
import FileSelectModal from './FileSelectModal.vue';
|
||||
import {message} from "ant-design-vue";
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { createLocalStorage } from '/@/utils/cache';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
const $ls = createLocalStorage();
|
||||
const { notification } = useMessage();
|
||||
|
||||
enum Api {
|
||||
tableInfo = '/online/cgform/head/tableInfo',
|
||||
codeGenerate = '/online/cgform/api/codeGenerate',
|
||||
downGenerateCode = '/online/cgform/api/downGenerateCode',
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CodeGenerator',
|
||||
components: { BasicForm, BasicModal, FileSelectModal, CodeFileListModal },
|
||||
emits: ['register'],
|
||||
setup(props) {
|
||||
const JEECG_ONL_PROJECT_PATH = 'JEECG_ONL_PROJECT_PATH';
|
||||
const JEECG_ONL_PROJECT_NAME = 'JEECG_ONL_PROJECT_NAME';
|
||||
const single = ref(true);
|
||||
const subTableRef = ref<JVxeTableInstance>();
|
||||
const modalWidth = computed(() => (single.value ? 800 : 1200));
|
||||
const title = ref('代码生成');
|
||||
const confirmLoading = ref(false);
|
||||
const { prefixCls } = useDesign('code-generator-modal');
|
||||
const wrapClassName = prefixCls;
|
||||
const code = ref('');
|
||||
const metaModel = reactive({
|
||||
projectPath: '',
|
||||
packageStyle: 'service',
|
||||
jspMode: '',
|
||||
jformType: '1',
|
||||
tableName_tmp: '',
|
||||
ftlDescription: '',
|
||||
entityName: '',
|
||||
codeTypes: 'controller,service,dao,mapper,entity,vue',
|
||||
});
|
||||
const model = reactive<Recordable>({});
|
||||
const jspModeOptions = ref<any[]>([]);
|
||||
// 子表配置
|
||||
const subTable = reactive({
|
||||
dataSource: [] as Recordable[],
|
||||
columns: [
|
||||
{
|
||||
title: '子表名',
|
||||
key: 'tableName',
|
||||
type: JVxeTypes.input,
|
||||
disabled: true,
|
||||
validateRules: [{ required: true, message: '请输入${title}' }],
|
||||
},
|
||||
{
|
||||
title: '子表实体',
|
||||
key: 'entityName',
|
||||
type: JVxeTypes.input,
|
||||
validateRules: [{ required: true, message: '请输入${title}' }],
|
||||
},
|
||||
{
|
||||
title: '功能说明',
|
||||
key: 'ftlDescription',
|
||||
type: JVxeTypes.input,
|
||||
validateRules: [{ required: true, message: '请输入${title}' }],
|
||||
},
|
||||
] as JVxeColumn[],
|
||||
});
|
||||
const showSubTable = computed<boolean>(() => subTable.dataSource.length > 0);
|
||||
|
||||
const { formSchemas } = useCodeGeneratorFormSchemas(
|
||||
props,
|
||||
{
|
||||
onProjectPathChange,
|
||||
onProjectPathSearch,
|
||||
jspModeOptions,
|
||||
},
|
||||
single
|
||||
);
|
||||
|
||||
// 表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
labelAlign: 'right',
|
||||
});
|
||||
|
||||
const [registerModal, { closeModal }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
// 当前主表ID
|
||||
code.value = data.code;
|
||||
confirmLoading.value = false;
|
||||
subTable.dataSource = [];
|
||||
jspModeOptions.value = [];
|
||||
getStoreProjectPath();
|
||||
Object.assign(model, metaModel);
|
||||
loadData();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
let { main, sub, jspModeList, projectPath } = await defHttp.get({
|
||||
url: Api.tableInfo,
|
||||
params: { code: code.value },
|
||||
});
|
||||
//update-begin-author:taoyan date:2022-5-17 for: vue3没有经典风格 默认是jvxe风格
|
||||
let jspModeListForVue3: any[] = [];
|
||||
for (let mode of jspModeList) {
|
||||
const { code, note } = mode;
|
||||
if (code == 'many') {
|
||||
//经典风格不需要
|
||||
} else {
|
||||
jspModeListForVue3.push({
|
||||
label: note,
|
||||
value: code,
|
||||
});
|
||||
}
|
||||
}
|
||||
jspModeOptions.value = jspModeListForVue3;
|
||||
if (main.isTree == 'Y') {
|
||||
model.jspMode = 'tree';
|
||||
} else {
|
||||
// 获取主表风格
|
||||
if (jspModeListForVue3.find(item => item.value === main.themeTemplate)) {
|
||||
model.jspMode = main.themeTemplate
|
||||
} else {
|
||||
// 如果没有找到默认选中第一个
|
||||
model.jspMode = jspModeListForVue3[0].value;
|
||||
}
|
||||
}
|
||||
//update-end-author:taoyan date:2022-5-17 for: vue3没有经典风格 默认是jvxe风格
|
||||
single.value = main.tableType == 1;
|
||||
title.value = '代码生成【' + main.tableName + '】';
|
||||
if (!model.projectPath) {
|
||||
model.projectPath = projectPath;
|
||||
window.localStorage.setItem(JEECG_ONL_PROJECT_PATH, projectPath);
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240611---for:【TV360X-1054】代码生成包名缓存
|
||||
const projectName = localStorage.getItem(JEECG_ONL_PROJECT_NAME);
|
||||
if (projectName) {
|
||||
model.entityPackage = projectName;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240611---for:【TV360X-1054】代码生成包名缓存
|
||||
model.jformType = main.tableType + '';
|
||||
model.tableName_tmp = main.tableName;
|
||||
model.ftlDescription = main.tableTxt;
|
||||
|
||||
let entityNameTemp = underLine2CamelCase(main.tableName);
|
||||
model.entityName = entityNameTemp.substring(0, 1).toUpperCase() + entityNameTemp.substring(1);
|
||||
await nextTick();
|
||||
setFieldsValue(model);
|
||||
if (sub && sub.length > 0) {
|
||||
subTable.dataSource = sub.map((item) => ({
|
||||
tableName: item.tableName,
|
||||
entityName: getCamelCase(item.tableName),
|
||||
ftlDescription: item.tableTxt,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// 生成代码文件后弹窗显示
|
||||
const [registerCodeFileListModal, {openModal: openCodeFileListModal}] = useModal();
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
let params = Object.assign({}, values, { code: code.value, tableName: values.tableName_tmp });
|
||||
if (showSubTable.value) {
|
||||
let errMap = await subTableRef.value!.validateTable();
|
||||
if (errMap) {
|
||||
return;
|
||||
}
|
||||
params.subList = subTableRef.value!.getTableData();
|
||||
}
|
||||
confirmLoading.value = true;
|
||||
// let codeList = await defHttp.post({ url: Api.codeGenerate, params });
|
||||
let res:any = await codeGen(params);
|
||||
//打开文件列表弹窗
|
||||
openCodeFileListModal(true, {
|
||||
codeList: res.codeList,
|
||||
pathKey: res.pathKey,
|
||||
tableName: values.tableName_tmp
|
||||
});
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
// 前端代码直接生成vue3项目,会自动加载刷新,给有效期10秒的临时提示信息
|
||||
const hasViewPath = res.codeList.some(
|
||||
s => s.includes('src/views/') || s.includes('src\\views\\')
|
||||
);
|
||||
if(hasViewPath){
|
||||
$ls.set(
|
||||
'code.genenrate.success.msg',
|
||||
`表【${values.tableName_tmp}】代码生成成功!前端代码已自动刷新,Java后台需重启生效!`,
|
||||
15
|
||||
);
|
||||
}else{
|
||||
notification.success({
|
||||
message: `表【${values.tableName_tmp}】代码生成成功`,
|
||||
description: h('div', [
|
||||
'1. 前端代码请迁移到 VUE3项目', h('br'),
|
||||
'2. 菜单SQL放到jeecg-system-start的flyway目录', h('br'),
|
||||
'3. Java 后台需重启生效'
|
||||
]),
|
||||
duration: 5,
|
||||
});
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------
|
||||
|
||||
closeModal();
|
||||
// update-begin--author:liaozhiyang---date:20240611---for:【TV360X-1054】代码生成包名缓存
|
||||
localStorage.setItem(JEECG_ONL_PROJECT_NAME, values.entityPackage);
|
||||
// update-end--author:liaozhiyang---date:20240611---for:【TV360X-1054】代码生成包名缓存
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function codeGen(params){
|
||||
return new Promise((resolve, reject) => {
|
||||
defHttp.post({ url: Api.codeGenerate, params }, {isTransformResponse: false}).then(res=>{
|
||||
if(res.success){
|
||||
let codeList = res.result;
|
||||
let pathKey = res.message;
|
||||
resolve({
|
||||
codeList,
|
||||
pathKey
|
||||
})
|
||||
}else{
|
||||
message.error(res.message);
|
||||
reject(res.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
// 注册文件选择弹窗
|
||||
const [registerFileSelectModal, fileSelectModal] = useModal();
|
||||
|
||||
function onProjectPathSearch() {
|
||||
fileSelectModal.openModal(true, {});
|
||||
}
|
||||
|
||||
function onFileSelect(url) {
|
||||
window.localStorage.setItem(JEECG_ONL_PROJECT_PATH, url);
|
||||
setFieldsValue({ projectPath: url });
|
||||
}
|
||||
|
||||
function getCamelCase(val) {
|
||||
let temp = underLine2CamelCase(val);
|
||||
return temp.substring(0, 1).toUpperCase() + temp.substring(1);
|
||||
}
|
||||
|
||||
function getStoreProjectPath() {
|
||||
let path = window.localStorage.getItem(JEECG_ONL_PROJECT_PATH);
|
||||
if (path) {
|
||||
metaModel.projectPath = path;
|
||||
}
|
||||
}
|
||||
|
||||
function onProjectPathChange(e) {
|
||||
if (e.target.value) window.localStorage.setItem(JEECG_ONL_PROJECT_PATH, e.target.value);
|
||||
}
|
||||
|
||||
return {
|
||||
title,
|
||||
modalWidth,
|
||||
confirmLoading,
|
||||
subTable,
|
||||
showSubTable,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
onFileSelect,
|
||||
registerFileSelectModal,
|
||||
subTableRef,
|
||||
registerForm,
|
||||
registerModal,
|
||||
registerCodeFileListModal,
|
||||
wrapClassName
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-code-generator-modal';
|
||||
.@{prefix-cls} {
|
||||
.jeecg-basic-form .ant-input {font-size: 14px;}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" :height="modalHeight" :width="500" title="从数据库导入表单" :confirmLoading="confirmLoading" @cancel="handleCancel" :wrapClassName="wrapClassName">
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection" @tableRedo="queryTables">
|
||||
<template #tableTitle>
|
||||
<div>
|
||||
注:导入表会排除配置前缀表
|
||||
<a href="http://doc.jeecg.com/2043924" target="_blank"> 参考文档</a>
|
||||
</div>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-spin>
|
||||
|
||||
<template #footer>
|
||||
<a-button @click="handleCancel">关闭</a-button>
|
||||
<a-button @click="handleTrans" type="primary" preIcon="ant-design:swap" :loading="confirmLoading || btnLoading"> 生成表单 </a-button>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, defineComponent } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { BasicTable } from '/@/components/Table';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { useListTable } from '/@/hooks/system/useListPage';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
enum Api {
|
||||
query = '/online/cgform/head/queryTables',
|
||||
trans = '/online/cgform/head/transTables/',
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TransDb2Online',
|
||||
components: { BasicModal, BasicTable },
|
||||
emits: ['success', 'register'],
|
||||
setup(_, { emit }) {
|
||||
const { createMessage: $message } = useMessage();
|
||||
const emptyText = ref('暂无数据');
|
||||
const confirmLoading = ref(false);
|
||||
const btnLoading = ref(false);
|
||||
const metaSource = ref<Recordable[]>([]);
|
||||
const dataSource = ref<Recordable[]>([]);
|
||||
|
||||
const { prefixCls } = useDesign('online-db-import-form-modal');
|
||||
const wrapClassName = prefixCls;
|
||||
const clientHeight = document.documentElement.clientHeight;
|
||||
const modalH = clientHeight - 180;
|
||||
const modalHeight = modalH > 690 ? 690 : modalH;
|
||||
|
||||
const [registerTable, { setPagination, getForm }, { rowSelection, selectedRowKeys }] = useListTable({
|
||||
bordered: true,
|
||||
columns: [{ title: '表名', align: 'left', dataIndex: 'id' }],
|
||||
dataSource: dataSource,
|
||||
maxHeight: clientHeight - 400,
|
||||
locale: { emptyText: emptyText },
|
||||
pagination: {
|
||||
showQuickJumper: false,
|
||||
showSizeChanger: false,
|
||||
},
|
||||
clickToRowSelect: true,
|
||||
showIndexColumn: true,
|
||||
showActionColumn: false,
|
||||
formConfig: {
|
||||
schemas: [
|
||||
{
|
||||
label: '表名',
|
||||
field: 'tableName',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
style: { width: '100%' },
|
||||
placeholder: '请输入表名以模糊筛选',
|
||||
onChange: (e) => searchFilter(e.target.value),
|
||||
},
|
||||
disabledLabelWidth: true,
|
||||
itemProps: {
|
||||
labelCol: { sm: 0, md: 0 },
|
||||
wrapperCol: { sm: 24, md: 20 },
|
||||
},
|
||||
},
|
||||
],
|
||||
baseColProps: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
|
||||
showActionButtonGroup: false,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerModal, { closeModal }] = useModalInner(() => {
|
||||
// 重置form
|
||||
getForm()?.resetFields();
|
||||
|
||||
btnLoading.value = false;
|
||||
emptyText.value = '暂无数据';
|
||||
selectedRowKeys.value = [];
|
||||
queryTables();
|
||||
});
|
||||
|
||||
function queryTables() {
|
||||
confirmLoading.value = true;
|
||||
return defHttp
|
||||
.get(
|
||||
{
|
||||
url: Api.query,
|
||||
},
|
||||
{
|
||||
errorMessageMode: 'none',
|
||||
}
|
||||
)
|
||||
.then(
|
||||
(result) => {
|
||||
dataSource.value = result;
|
||||
metaSource.value = [...result];
|
||||
return result;
|
||||
},
|
||||
(e) => {
|
||||
if (e.message == 'noadminauth') {
|
||||
emptyText.value = '非admin用户无权限操作!';
|
||||
$message.warn(emptyText.value);
|
||||
} else {
|
||||
console.error(e);
|
||||
}
|
||||
dataSource.value = [];
|
||||
metaSource.value = [];
|
||||
}
|
||||
)
|
||||
.finally(() => {
|
||||
confirmLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function searchFilter(keyword) {
|
||||
if (metaSource.value.length === 0) return;
|
||||
if (!keyword) {
|
||||
dataSource.value = [...metaSource.value];
|
||||
} else {
|
||||
dataSource.value = metaSource.value.filter((item) => item.id.toLowerCase().includes(keyword.toLowerCase()));
|
||||
emptyText.value = dataSource.value.length === 0 ? '无筛选结果' : '暂无数据';
|
||||
}
|
||||
setPagination({ current: 1 });
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function handleTrans() {
|
||||
if (!selectedRowKeys.value || selectedRowKeys.value.length == 0) {
|
||||
$message.warning('请选择一张表');
|
||||
return;
|
||||
} else {
|
||||
btnLoading.value = true;
|
||||
let tbNames = selectedRowKeys.value.join(',');
|
||||
defHttp
|
||||
.post({url: Api.trans + tbNames}, {errorMessageMode: "modal"})
|
||||
.then(() => {
|
||||
closeModal();
|
||||
})
|
||||
.finally(() => {
|
||||
btnLoading.value = false
|
||||
emit('success')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
emptyText,
|
||||
confirmLoading,
|
||||
btnLoading,
|
||||
metaSource,
|
||||
handleTrans,
|
||||
handleCancel,
|
||||
queryTables,
|
||||
registerModal,
|
||||
registerTable,
|
||||
rowSelection,
|
||||
selectedRowKeys,
|
||||
wrapClassName,
|
||||
modalHeight,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
// update-begin--author:liaozhiyang---date:20240110---for:【QQYUN-7838】online导入到表界面优化
|
||||
@prefix-cls: ~'@{namespace}-online-db-import-form-modal';
|
||||
.@{prefix-cls} {
|
||||
.ant-table-wrapper .ant-table-pagination.ant-pagination {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.jeecg-basic-table-form-container {
|
||||
padding-bottom: 0;
|
||||
.ant-form {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.jeecg-basic-table .ant-table-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
.ant-form-item-label {width:40px;}
|
||||
.ant-modal {top: 20px;}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240110---for:【QQYUN-7838】online导入到表界面优化
|
||||
</style>
|
||||
@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<ConfigProvider :theme="{ token: { fontSize: 13 } }">
|
||||
<BasicModal @register="registerModal" title="表单扩展配置项" :width="1000" @ok="handleOk" @cancel="handleCancel">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</ConfigProvider>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ConfigProvider } from 'ant-design-vue';
|
||||
import { nextTick, defineComponent } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useExtendConfigFormSchemas } from '../hooks/useSchemas';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CgformExtConfigModel',
|
||||
components: { BasicModal, BasicForm, ConfigProvider },
|
||||
props: {
|
||||
parentForm: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ['register', 'ok'],
|
||||
setup(props, { emit }) {
|
||||
const { createMessage: $message } = useMessage();
|
||||
|
||||
const { formSchemas } = useExtendConfigFormSchemas(props, {
|
||||
onIsDesformChange,
|
||||
onJoinQueryChange,
|
||||
onReportPrintShowChange,
|
||||
onFormLabelLengthShow,
|
||||
});
|
||||
|
||||
// 表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, getFieldsValue, clearValidate, validate }] = useForm({
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
labelAlign: 'right',
|
||||
});
|
||||
|
||||
const [registerModal, { closeModal }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
await setFieldsValue(data.extConfigJson);
|
||||
});
|
||||
|
||||
async function handleOk() {
|
||||
await clearValidate();
|
||||
await nextTick();
|
||||
try {
|
||||
const values = await validate();
|
||||
emit('ok', values);
|
||||
closeModal();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
// 对接表单设计更改事件
|
||||
function onIsDesformChange(value) {
|
||||
if (value === 'Y') {
|
||||
let { themeTemplate } = props.parentForm.getFieldsValue(['themeTemplate']);
|
||||
if ('erp' === themeTemplate) {
|
||||
props.parentForm.setFieldsValue({ themeTemplate: 'normal' });
|
||||
$message.warning('请注意:erp风格不支持对接表单设计,已自动改为默认风格!');
|
||||
}
|
||||
} else {
|
||||
clearValidate('desFormCode');
|
||||
}
|
||||
}
|
||||
|
||||
// 默认提示积木报表地址
|
||||
const defaultReportPrintUrl = `{{ window._CONFIG['domianURL'] }}/jmreport/view/{积木报表ID}`;
|
||||
|
||||
// 集成积木报表更改事件
|
||||
async function onReportPrintShowChange(value) {
|
||||
let reportPrintUrl = getFieldsValue()['reportPrintUrl'];
|
||||
// 0 = 关闭时,清空默认提示地址
|
||||
if (value === 0) {
|
||||
if (reportPrintUrl === defaultReportPrintUrl) {
|
||||
await setFieldsValue({ reportPrintUrl: '' });
|
||||
}
|
||||
} else if (value === 1) {
|
||||
if (reportPrintUrl === '') {
|
||||
await setFieldsValue({ reportPrintUrl: defaultReportPrintUrl });
|
||||
}
|
||||
}
|
||||
clearValidate('reportPrintUrl');
|
||||
}
|
||||
/**
|
||||
* 2024-03-29
|
||||
* liaozhiyang
|
||||
* 表单统一label长度关闭时,formLabelLength置空
|
||||
*/
|
||||
async function onFormLabelLengthShow(value) {
|
||||
if (value == 0) {
|
||||
await setFieldsValue({ formLabelLength: null });
|
||||
}
|
||||
await clearValidate('formLabelLength');
|
||||
}
|
||||
|
||||
// 联合查询更改事件
|
||||
function onJoinQueryChange(value) {
|
||||
if (value === 1) {
|
||||
let { themeTemplate, isTree, tableType } = props.parentForm.getFieldsValue(['themeTemplate', 'isTree', 'tableType']);
|
||||
if ('erp' === themeTemplate) {
|
||||
$message.warning('请注意:erp风格不支持联合查询,配置无效!');
|
||||
setFieldsValue({ joinQuery: 0 });
|
||||
}
|
||||
if ('innerTable' === themeTemplate) {
|
||||
$message.warning('请注意:内嵌风格不支持联合查询,配置无效!');
|
||||
setFieldsValue({ joinQuery: 0 });
|
||||
}
|
||||
if (1 === tableType) {
|
||||
$message.warning('请注意:单表不支持联合查询,配置无效!');
|
||||
setFieldsValue({ joinQuery: 0 });
|
||||
} else if (3 === tableType) {
|
||||
$message.warning('请注意:当前表为附表,请在对应主表配置!');
|
||||
setFieldsValue({ joinQuery: 0 });
|
||||
} else if ('Y' === isTree) {
|
||||
$message.warning('请注意:树形列表不支持联合查询,配置无效!');
|
||||
setFieldsValue({ joinQuery: 0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
handleOk,
|
||||
handleCancel,
|
||||
registerModal,
|
||||
registerForm,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.onl-cgform-ext-config-form.ant-form-inline {
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" title="选择目录" :width="500" @ok="onSubmit" @cancel="onCancel">
|
||||
<a-spin :spinning="loading">
|
||||
<div class="btnArea">
|
||||
<a-button @click="hanldeRefresh">刷新</a-button>
|
||||
</div>
|
||||
<a-directory-tree v-if="directoryTreeShow" :treeData="treeData" :loadData="onLoadData" @select="onSelect" />
|
||||
</a-spin>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
enum Api {
|
||||
fileTreeUrl = '/online/cgform/head/fileTree',
|
||||
rootFileUrl = '/online/cgform/head/rootFile',
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FileSelectModal',
|
||||
components: { BasicModal },
|
||||
emits: ['select', 'register'],
|
||||
setup(_, { emit }) {
|
||||
const loading = ref(true);
|
||||
const treeData = ref<any[]>([]);
|
||||
const selectedKey = ref('');
|
||||
const directoryTreeShow = ref(false);
|
||||
|
||||
const [registerModal, { closeModal }] = useModalInner(async () => {
|
||||
selectedKey.value = '';
|
||||
if (treeData.value.length === 0) {
|
||||
loadRoot();
|
||||
}
|
||||
});
|
||||
|
||||
function onSubmit() {
|
||||
emit('select', selectedKey.value);
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
async function loadRoot() {
|
||||
loading.value = true;
|
||||
treeData.value = await defHttp.get({ url: Api.rootFileUrl }).finally(() => {
|
||||
loading.value = false;
|
||||
directoryTreeShow.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
async function onLoadData(treeNode) {
|
||||
if (treeNode.dataRef.children) {
|
||||
return;
|
||||
}
|
||||
let params = {
|
||||
parentPath: treeNode.dataRef.key,
|
||||
};
|
||||
treeNode.dataRef.children = await defHttp.get({ url: Api.fileTreeUrl, params });
|
||||
treeData.value = [...treeData.value];
|
||||
}
|
||||
|
||||
function onSelect(selectedKeys) {
|
||||
selectedKey.value = selectedKeys[0];
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20231017---for:【QQYUN-6102】新建文件夹需要刷新
|
||||
const hanldeRefresh = () => {
|
||||
selectedKey.value = '';
|
||||
directoryTreeShow.value = false;
|
||||
loadRoot();
|
||||
};
|
||||
// update-begin--author:liaozhiyang---date:20231017---for:【QQYUN-6102】新建文件夹需要刷新
|
||||
return { loading, treeData, onLoadData, onSelect, onSubmit, onCancel, registerModal, hanldeRefresh, directoryTreeShow };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.btnArea {
|
||||
margin-bottom: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<BasicDrawer @register="registerDrawer" title="权限管理" :width="800" @close="onClose">
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane tab="字段权限" key="field" forceRender>
|
||||
<AuthFieldConfig :headId="headId" v-model:authFields="authFields" />
|
||||
</a-tab-pane>
|
||||
<template v-if="hasDataAuth">
|
||||
<a-tab-pane tab="按钮权限" key="button" forceRender>
|
||||
<AuthButtonConfig :headId="headId" :tableType="curTableType" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="数据权限" key="data" forceRender>
|
||||
<!-- 数据权限不需要实时刷新 故而此处传原表单ID -->
|
||||
<AuthDataConfig :cgformId="cgformId" :authFields="authFields" />
|
||||
</a-tab-pane>
|
||||
</template>
|
||||
</a-tabs>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import AuthFieldConfig from './manager/AuthFieldConfig.vue';
|
||||
import AuthButtonConfig from './manager/AuthButtonConfig.vue';
|
||||
import AuthDataConfig from './manager/AuthDataConfig.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AuthManagerDrawer',
|
||||
components: {
|
||||
BasicDrawer,
|
||||
AuthFieldConfig,
|
||||
AuthButtonConfig,
|
||||
AuthDataConfig,
|
||||
},
|
||||
props: {
|
||||
// 1单表 2主表 3附表
|
||||
tableType: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
emits: ['register'],
|
||||
setup(props) {
|
||||
const cgformId = ref('');
|
||||
const headId = ref('');
|
||||
const authFields = ref([]);
|
||||
const activeKey = ref('field');
|
||||
const curTableType = ref(1);
|
||||
const hasDataAuth = computed(() => props.tableType == 1 || props.tableType == 2);
|
||||
|
||||
const [registerDrawer, { closeDrawer }] = useDrawerInner((data) => {
|
||||
cgformId.value = data.cgformId;
|
||||
headId.value = cgformId.value + '?' + new Date().getTime();
|
||||
activeKey.value = 'field';
|
||||
// update-begin--author:liaozhiyang---date:20240520---for:【TV360X-187】去掉子表权限管理中按钮权限的高级查询
|
||||
curTableType.value = data.tableType;
|
||||
// update-end--author:liaozhiyang---date:20240520---for:【TV360X-187】去掉子表权限管理中按钮权限的高级查询
|
||||
});
|
||||
|
||||
function onClose() {
|
||||
closeDrawer();
|
||||
}
|
||||
|
||||
return {
|
||||
activeKey,
|
||||
cgformId,
|
||||
headId,
|
||||
authFields,
|
||||
hasDataAuth,
|
||||
onClose,
|
||||
registerDrawer,
|
||||
curTableType,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,232 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
title="Online权限授权"
|
||||
:width="900"
|
||||
:maskClosable="false"
|
||||
defaultFullscreen
|
||||
:okButtonProps="{ style: { display: 'none' } }"
|
||||
cancelText="关闭"
|
||||
@cancel="closeModal"
|
||||
@register="registerModal"
|
||||
@open-change="hanldeOpenChange"
|
||||
>
|
||||
<a-spin wrapperClassName="authsetting-container" :spinning="loading">
|
||||
<a-row v-if="contentShow">
|
||||
<a-col :span="12">
|
||||
<a-tabs v-model:activeKey="authMode" @change="onAuthModeChange">
|
||||
<a-tab-pane tab="角色授权" key="role" forceRender>
|
||||
<LeftRole ref="roleRef" @select="onSelectRole" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="部门授权" key="depart" forceRender>
|
||||
<LeftDepart ref="departRef" @select="onSelectDepart" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="人员授权" key="user" forceRender>
|
||||
<LeftUser ref="userRef" @select="onSelectUser" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-col>
|
||||
<a-col :span="1"></a-col>
|
||||
<a-col :span="11">
|
||||
<a-tabs v-model:activeKey="activeKey" @change="onAuthTypeChange">
|
||||
<a-tab-pane tab="字段权限" key="field" forceRender>
|
||||
<AuthFieldTree class="authFieldTree" ref="fieldRef" :cgformId="cgformId" />
|
||||
</a-tab-pane>
|
||||
<template v-if="hasDataAuth">
|
||||
<a-tab-pane tab="按钮权限" key="button" forceRender>
|
||||
<AuthButtonTree class="authButtonTree" ref="buttonRef" :cgformId="cgformId" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="数据权限" key="data" forceRender>
|
||||
<AuthDataTree class="authDataTree" ref="dataRef" :cgformId="cgformId" />
|
||||
</a-tab-pane>
|
||||
</template>
|
||||
</a-tabs>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-spin>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import LeftRole from './setter/LeftRole.vue';
|
||||
import LeftDepart from './setter/LeftDepart.vue';
|
||||
import LeftUser from './setter/LeftUser.vue';
|
||||
import AuthFieldTree from './setter/AuthFieldTree.vue';
|
||||
import AuthButtonTree from './setter/AuthButtonTree.vue';
|
||||
import AuthDataTree from './setter/AuthDataTree.vue';
|
||||
|
||||
import { ref, computed, nextTick, defineComponent } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AuthSetterModal',
|
||||
components: {
|
||||
BasicModal,
|
||||
LeftRole,
|
||||
LeftDepart,
|
||||
LeftUser,
|
||||
AuthFieldTree,
|
||||
AuthButtonTree,
|
||||
AuthDataTree,
|
||||
},
|
||||
props: {
|
||||
// 1单表 2主表 3附表
|
||||
tableType: { type: Number, default: 1 },
|
||||
},
|
||||
setup(props) {
|
||||
const cgformId = ref('');
|
||||
const loading = ref(false);
|
||||
const activeKey = ref('field');
|
||||
const authMode = ref('role');
|
||||
const refs = {
|
||||
fieldRef: ref(),
|
||||
buttonRef: ref(),
|
||||
dataRef: ref(),
|
||||
roleRef: ref(),
|
||||
userRef: ref(),
|
||||
departRef: ref(),
|
||||
};
|
||||
const activeRole = ref('');
|
||||
const hasDataAuth = computed(() => props.tableType == 1 || props.tableType == 2);
|
||||
const contentShow = ref(true);
|
||||
// 表单赋值
|
||||
const [registerModal, { closeModal }] = useModalInner((data) => {
|
||||
activeKey.value = 'field';
|
||||
// update-begin--author:liaozhiyang---date:20240520---for:【TV360X-147】权限设置重置到角色tab
|
||||
authMode.value = 'role';
|
||||
// update-end--author:liaozhiyang---date:20240520---for:【TV360X-147】权限设置重置到角色tab
|
||||
// QQYUN-4285【online表单】字段权限 勾选后不保存,关闭再次打开 还是选中状态
|
||||
cgformId.value = data.cgformId;
|
||||
reset();
|
||||
});
|
||||
|
||||
function getActiveRef<T = any>(key = activeKey.value): T {
|
||||
return refs[key + 'Ref']?.value;
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
await nextTick();
|
||||
clearLeftCurrentTabSelect();
|
||||
getActiveRef().clear();
|
||||
}
|
||||
|
||||
// 选中角色事件
|
||||
function onSelectRole(roleId) {
|
||||
activeRole.value = roleId;
|
||||
onAuthTypeChange(activeKey.value);
|
||||
clearLeftOtherTabSelect();
|
||||
}
|
||||
|
||||
// 选中部门事件
|
||||
function onSelectDepart(departid) {
|
||||
activeRole.value = departid;
|
||||
onAuthTypeChange(activeKey.value);
|
||||
clearLeftOtherTabSelect();
|
||||
}
|
||||
|
||||
// 选中用户事件
|
||||
function onSelectUser(userId) {
|
||||
activeRole.value = userId;
|
||||
onAuthTypeChange(activeKey.value);
|
||||
clearLeftOtherTabSelect();
|
||||
}
|
||||
|
||||
// 清空左侧选中
|
||||
function clearLeftOtherTabSelect() {
|
||||
if (authMode.value == 'role') {
|
||||
refs.departRef.value.clearSelected();
|
||||
refs.userRef.value.clearSelected();
|
||||
} else if (authMode.value == 'depart') {
|
||||
refs.roleRef.value.clearSelected();
|
||||
refs.userRef.value.clearSelected();
|
||||
} else if (authMode.value == 'user') {
|
||||
refs.departRef.value.clearSelected();
|
||||
refs.roleRef.value.clearSelected();
|
||||
}
|
||||
}
|
||||
|
||||
function clearLeftCurrentTabSelect() {
|
||||
if (authMode.value == 'role') {
|
||||
refs.roleRef.value.clearSelected();
|
||||
} else if (authMode.value == 'depart') {
|
||||
refs.departRef.value.clearSelected();
|
||||
} else if (authMode.value == 'user') {
|
||||
refs.userRef.value.clearSelected();
|
||||
}
|
||||
getActiveRef().clearChecked();
|
||||
// update-begin--author:liaozhiyang---date:20231226---for:【QQYUN-7543】左侧没选中角色、部门、人员时,右侧展示空
|
||||
activeRole.value = '';
|
||||
// update-end--author:liaozhiyang---date:20231226---for:【QQYUN-7543】左侧没选中角色、部门、人员时,右侧展示空
|
||||
}
|
||||
|
||||
// 右侧授权类型切换 事件
|
||||
async function onAuthTypeChange(key) {
|
||||
// 切换 右侧tab 如果当前选中角色信息需要加载tab内选中信息
|
||||
await nextTick();
|
||||
if (activeRole.value) {
|
||||
getActiveRef(key).loadChecked(activeRole.value, authMode.value);
|
||||
}
|
||||
}
|
||||
|
||||
// 左侧授权方式切换 事件
|
||||
function onAuthModeChange() {
|
||||
clearLeftCurrentTabSelect();
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240523---for:【TV360X-239】权限授权页面角色分页再次打开弹框时没有初始化为第一页
|
||||
const hanldeOpenChange = (open: boolean) => {
|
||||
contentShow.value = open;
|
||||
};
|
||||
// update-end--author:liaozhiyang---date:20240523---for:【TV360X-239】权限授权页面角色分页再次打开弹框时没有初始化为第一页
|
||||
return {
|
||||
...refs,
|
||||
cgformId,
|
||||
loading,
|
||||
activeKey,
|
||||
hasDataAuth,
|
||||
authMode,
|
||||
onAuthModeChange,
|
||||
onAuthTypeChange,
|
||||
closeModal,
|
||||
onSelectRole,
|
||||
onSelectDepart,
|
||||
onSelectUser,
|
||||
registerModal,
|
||||
hanldeOpenChange,
|
||||
contentShow,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// update-begin--author:liaozhiyang---date:20231226---for:【QQYUN-7540】角色权限弹窗右侧内容较长时局部滚动
|
||||
.authsetting-container {
|
||||
height: 100%;
|
||||
:deep(.ant-spin-container) {
|
||||
height: 100%;
|
||||
}
|
||||
.ant-row,
|
||||
.ant-col {
|
||||
height: 100%;
|
||||
}
|
||||
.ant-tabs {
|
||||
height: 100%;
|
||||
:deep(.ant-tabs-content) {
|
||||
height: 100%;
|
||||
}
|
||||
.ant-tabs-tabpane {
|
||||
height: 100%;
|
||||
.authFieldTree, .authButtonTree, .authDataTree {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
:deep(.ant-tree) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20231226---for:【QQYUN-7540】角色权限弹窗右侧内容较长时局部滚动
|
||||
</style>
|
||||
@ -0,0 +1,81 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
export enum Api {
|
||||
authField = '/online/cgform/api/authColumn',
|
||||
authButton = '/online/cgform/api/authButton',
|
||||
authData = '/online/cgform/api/authData',
|
||||
authPage = '/online/cgform/api/authPage',
|
||||
roleAuth = '/online/cgform/api/roleAuth',
|
||||
saveButton = '/online/cgform/api/roleButtonAuth',
|
||||
saveData = '/online/cgform/api/roleDataAuth',
|
||||
validData = '/online/cgform/api/validAuthData',
|
||||
saveField = '/online/cgform/api/roleColumnAuth',
|
||||
batchAuthField = '/online/cgform/api/authColumn/batch',
|
||||
}
|
||||
|
||||
// 字段权限,查询数据
|
||||
export const authFieldLoadData = (cgformId, params?) => defHttp.get({ url: `${Api.authField}/${cgformId}`, params });
|
||||
// 字段权限,更新启用状态
|
||||
export const authFieldUpdateStatus = (params) => defHttp.put({ url: Api.authField, params });
|
||||
// 字段权限,更新权限状态
|
||||
export const authFieldUpdateCheckbox = (params) => defHttp.post({ url: Api.authField, params });
|
||||
|
||||
// 字段权限,批量更新启用状态
|
||||
export const batchAuthFieldUpdateStatus = (params) => defHttp.put({ url: Api.batchAuthField, params });
|
||||
// 字段权限,批量更新权限状态
|
||||
export const batchAuthFieldUpdateCheckbox = (params) => defHttp.post({ url: Api.batchAuthField, params });
|
||||
|
||||
|
||||
// 按钮权限,查询数据
|
||||
export const authButtonLoadData = (cgformId, params?) => defHttp.get({ url: `${Api.authButton}/${cgformId}`, params });
|
||||
// 按钮权限,启用
|
||||
export const authButtonEnable = (params) => defHttp.post({ url: Api.authButton, params });
|
||||
// 按钮权限,禁用
|
||||
export const authButtonDisable = (id: string, params?) => defHttp.put({ url: `${Api.authButton}/${id}`, params });
|
||||
|
||||
// 数据权限,查询数据
|
||||
export const authDataLoadData = (cgformId, params?) => defHttp.get({ url: `${Api.authData}/${cgformId}`, params });
|
||||
// 数据权限,更新启用状态
|
||||
export const authDataUpdateStatus = (params) => defHttp.put({ url: Api.authData, params });
|
||||
// 数据权限,保存或修改
|
||||
export const authDataSaveOrUpdate = (params, isUpdate: boolean) => {
|
||||
if (isUpdate) {
|
||||
return defHttp.put({ url: Api.authData, params });
|
||||
} else {
|
||||
return defHttp.post({ url: Api.authData, params });
|
||||
}
|
||||
};
|
||||
// 数据权限,删除
|
||||
export const authDataDelete = (id: string, params?) => defHttp.delete({ url: `${Api.authData}/${id}`, params });
|
||||
|
||||
export const authFieldLoadTree = (cgformId: string, authType: number, params?) => {
|
||||
let url = `${Api.authPage}/${cgformId}/${authType}`;
|
||||
return defHttp.get({ url, params });
|
||||
};
|
||||
|
||||
export const authDataLoadTree = (cgformId: string, params?) => {
|
||||
let url = `${Api.validData}/${cgformId}`;
|
||||
return defHttp.get({ url, params });
|
||||
};
|
||||
|
||||
export const authButtonLoadTree = (cgformId: string, authType: number, params?) => {
|
||||
let url = `${Api.authPage}/${cgformId}/${authType}`;
|
||||
return defHttp.get({ url, params });
|
||||
};
|
||||
|
||||
export const loadRoleAuthChecked = (params) => defHttp.get({ url: Api.roleAuth, params });
|
||||
|
||||
export const saveAuthField = (roleId: string, cgformId: string, params?) => {
|
||||
let url = `${Api.saveField}/${roleId}/${cgformId}`;
|
||||
return defHttp.post({ url, params });
|
||||
};
|
||||
|
||||
export const saveAuthData = (roleId: string, cgformId: string, params?) => {
|
||||
let url = `${Api.saveData}/${roleId}/${cgformId}`;
|
||||
return defHttp.post({ url, params });
|
||||
};
|
||||
|
||||
export const saveAuthButton = (roleId: string, cgformId: string, params?) => {
|
||||
let url = `${Api.saveButton}/${roleId}/${cgformId}`;
|
||||
return defHttp.post({url, params}, {successMessageMode: 'none', isTransformResponse: false});
|
||||
};
|
||||
@ -0,0 +1,235 @@
|
||||
import { computed } from 'vue';
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { useConditionFilter } from '/@/utils/index';
|
||||
|
||||
// 字段权限列配置
|
||||
export const authFieldColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '启用',
|
||||
dataIndex: 'switch',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
slots: { customRender: 'switch' },
|
||||
},
|
||||
{
|
||||
title: '字段名称',
|
||||
width: 200,
|
||||
dataIndex: 'code',
|
||||
},
|
||||
{
|
||||
title: '字段描述',
|
||||
// width: 200,
|
||||
dataIndex: 'title',
|
||||
},
|
||||
{
|
||||
title: '列表控制',
|
||||
dataIndex: 'list',
|
||||
width: 120,
|
||||
slots: { customRender: 'list' },
|
||||
},
|
||||
{
|
||||
title: '表单控制',
|
||||
dataIndex: 'form',
|
||||
width: 180,
|
||||
slots: { customRender: 'form' },
|
||||
},
|
||||
];
|
||||
|
||||
// 按钮权限列配置
|
||||
export const authButtonColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '启用',
|
||||
dataIndex: 'switch',
|
||||
width: 80,
|
||||
slots: { customRender: 'switch' },
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'title',
|
||||
},
|
||||
{
|
||||
title: '编码',
|
||||
dataIndex: 'code',
|
||||
},
|
||||
{
|
||||
title: '权限控制',
|
||||
dataIndex: 'control',
|
||||
width: 180,
|
||||
slots: { customRender: 'control' },
|
||||
},
|
||||
];
|
||||
|
||||
export const authButtonFixedList = [
|
||||
{ code: 'add', title: '新增', status: 0 },
|
||||
{ code: 'edit', title: '编辑', status: 0 },
|
||||
{ code: 'detail', title: '详情', status: 0 },
|
||||
{ code: 'delete', title: '删除', status: 0 },
|
||||
{ code: 'batch_delete', title: '批量删除', status: 0 },
|
||||
{ code: 'export', title: '导出', status: 0 },
|
||||
{ code: 'import', title: '导入', status: 0 },
|
||||
{ code: 'query', title: '查询', status: 0 },
|
||||
{ code: 'reset', title: '重置', status: 0 },
|
||||
{ code: 'aigc_mock_data', title: '生成测试数据', status: 0 },
|
||||
{ code: 'bpm', title: '提交流程', status: 0 },
|
||||
{ code: 'super_query', title: '高级查询', status: 0 },
|
||||
{ code: 'form_confirm', title: '确定', status: 0 },
|
||||
];
|
||||
|
||||
export const USE_SQL_RULES = 'USE_SQL_RULES';
|
||||
// 数据权限列配置
|
||||
export const authDataColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '启用',
|
||||
dataIndex: 'switch',
|
||||
width: 80,
|
||||
slots: { customRender: 'switch' },
|
||||
},
|
||||
{
|
||||
title: '规则名称',
|
||||
dataIndex: 'ruleName',
|
||||
width: 130,
|
||||
},
|
||||
{
|
||||
title: '规则描述',
|
||||
dataIndex: 'description',
|
||||
customRender({ record: { ruleOperator, ruleValue, ruleColumn } }) {
|
||||
if (ruleOperator == USE_SQL_RULES) {
|
||||
return `自定义SQL: ${ruleValue}`;
|
||||
} else {
|
||||
return `${ruleColumn} ${ruleOperator} ${ruleValue}`;
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function useAuthDataFormSchemas(props, methods) {
|
||||
const formSchemas = computed<FormSchema[]>(() => [
|
||||
{
|
||||
label: '规则名称',
|
||||
field: 'ruleName',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
onChange: methods.onRuleNameChange,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '规则字段',
|
||||
field: 'ruleColumn',
|
||||
component: 'JSearchSelect',
|
||||
componentProps: {
|
||||
dictOptions: props.authFields,
|
||||
getPopupContainer: () => document.body,
|
||||
onChange: methods.onRuleColumnChange,
|
||||
},
|
||||
dynamicRules({ model }) {
|
||||
return [{ required: model.ruleOperator != USE_SQL_RULES, message: '请输入规则字段' }];
|
||||
},
|
||||
show: ({ model }) => model.ruleOperator != USE_SQL_RULES,
|
||||
},
|
||||
// -update-begin--author:liaozhiyang---date:20240617---for:【TV360X-201】权限管理条件根据控件过滤
|
||||
{
|
||||
label: '条件规则',
|
||||
field: 'ruleOperator',
|
||||
required: true,
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
options: [],
|
||||
onChange: methods.onRuleOperatorChange,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
dynamicPropskey: 'options',
|
||||
dynamicPropsVal: ({ model, field }) => {
|
||||
const getFieldType = (type) => {
|
||||
if (['BigDecimal', 'double', 'int'].includes(type)) {
|
||||
return 'number';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
const { filterCondition } = useConditionFilter();
|
||||
if (model.ruleColumn) {
|
||||
const findItem = props.authFields.find((item) => item.value === model.ruleColumn) ?? {};
|
||||
const result = filterCondition({ view: findItem.view, fieldType: getFieldType(findItem.dbType) }).map((item) => ({
|
||||
label: item.title ?? item.label,
|
||||
value: item.val ?? item.value,
|
||||
}));
|
||||
result.push({ value: 'USE_SQL_RULES', label: '自定义SQL' });
|
||||
return result;
|
||||
} else {
|
||||
return [{ value: 'USE_SQL_RULES', label: '自定义SQL' }];
|
||||
}
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: '条件规则',
|
||||
// field: 'ruleOperator',
|
||||
// required: true,
|
||||
// component: 'JDictSelectTag',
|
||||
// componentProps: {
|
||||
// dictCode: 'rule_conditions',
|
||||
// onChange: methods.onRuleOperatorChange,
|
||||
// getPopupContainer: () => document.body,
|
||||
// },
|
||||
// },
|
||||
// -update-end--author:liaozhiyang---date:20240617---for:【TV360X-201】权限管理条件根据控件过滤
|
||||
{
|
||||
label: '规则值',
|
||||
field: 'ruleValue',
|
||||
required: true,
|
||||
// -update-begin--author:liaozhiyang---date:20240607---for:【TV360X-536】数据权限配置配置优化及新增JInputSelect组件
|
||||
component: 'JInputSelect',
|
||||
componentProps: {
|
||||
selectPlaceholder: '可选择系统变量',
|
||||
inputPlaceholder: '请输入',
|
||||
getPopupContainer: () => document.body,
|
||||
selectWidth: '200px',
|
||||
options: [
|
||||
{
|
||||
label: '登录用户账号',
|
||||
value: '#{sys_user_code}',
|
||||
},
|
||||
{
|
||||
label: '登录用户名称',
|
||||
value: '#{sys_user_name}',
|
||||
},
|
||||
{
|
||||
label: '当前日期',
|
||||
value: '#{sys_date}',
|
||||
},
|
||||
{
|
||||
label: '当前时间',
|
||||
value: '#{sys_time}',
|
||||
},
|
||||
{
|
||||
label: '登录用户部门',
|
||||
value: '#{sys_org_code}',
|
||||
},
|
||||
{
|
||||
label: '用户拥有的部门',
|
||||
value: '#{sys_multi_org_code}',
|
||||
},
|
||||
{
|
||||
label: '登录用户租户',
|
||||
value: '#{tenant_id}',
|
||||
},
|
||||
],
|
||||
},
|
||||
// -update-end--author:liaozhiyang---date:20240607---for:【TV360X-536】数据权限配置配置优化及新增JInputSelect组件
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
field: 'status',
|
||||
required: true,
|
||||
component: 'RadioButtonGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '有效', value: 1 },
|
||||
{ label: '无效', value: 0 },
|
||||
],
|
||||
},
|
||||
defaultValue: 1,
|
||||
},
|
||||
]);
|
||||
return { formSchemas };
|
||||
}
|
||||
@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div class="auth-field-config">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #switch="{ text, record }">
|
||||
<a-switch size="small" :checked="record.status === 1" @change="(flag) => onUpdateStatus(flag, record)" />
|
||||
</template>
|
||||
|
||||
<template #control> 可见 </template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { BasicTable, useTable } from '/@/components/Table';
|
||||
import { authButtonLoadData, authButtonEnable, authButtonDisable } from '../auth.api';
|
||||
import { authButtonColumns, authButtonFixedList } from '../auth.data';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AuthButtonConfig',
|
||||
components: { BasicTable },
|
||||
props: {
|
||||
headId: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
// 1单表 2主表 3附表
|
||||
tableType: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const cgformId = ref('');
|
||||
const pageType = ref(2);
|
||||
const pageControlList = ref(3);
|
||||
const pageControlForm = ref(5);
|
||||
const [registerTable, { reload, getTableRef, setPagination }] = useTable({
|
||||
api: loadData,
|
||||
rowKey: 'code',
|
||||
bordered: true,
|
||||
columns: authButtonColumns,
|
||||
showIndexColumn: false,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.headId,
|
||||
(headId) => {
|
||||
cgformId.value = headId.split('?')[0];
|
||||
// update-begin--author:liaozhiyang---date:20240520---for:【TV360X-149】点击权限控制进入页面后,分页没有重置
|
||||
getTableRef().value && setPagination({ current: 1, pageSize: 10 });
|
||||
// update-end--author:liaozhiyang---date:20240520---for:【TV360X-149】点击权限控制进入页面后,分页没有重置
|
||||
reload().catch(() => null);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取内置按钮
|
||||
* liaozhiyang
|
||||
* 2024-06-14
|
||||
* 【TV360X-1017】子表权限管理按钮权限根据主表主题模版只显示所需按钮
|
||||
* */
|
||||
const getButtonList = (res) => {
|
||||
const buttons = cloneDeep(authButtonFixedList);
|
||||
if (res.mainRelationType != null && res.mainThemeTemplate != null && props.tableType == 3) {
|
||||
// 子表(一对一、一对多)
|
||||
let result: any = [];
|
||||
switch (res.mainThemeTemplate) {
|
||||
case 'normal':
|
||||
case 'innerTable':
|
||||
case 'tab':
|
||||
if (res.mainRelationType == 1) {
|
||||
// 一对一
|
||||
result = [];
|
||||
} else {
|
||||
// 一对多
|
||||
result = buttons.filter((item) => ['add', 'update', 'batch_delete'].includes(item.code));
|
||||
}
|
||||
break;
|
||||
case 'erp':
|
||||
result = buttons.filter((item) => !['super_query'].includes(item.code));
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
// 主表\单表(全显示)
|
||||
return buttons;
|
||||
}
|
||||
};
|
||||
|
||||
// 加载数据
|
||||
async function loadData(params) {
|
||||
let result = await authButtonLoadData(cgformId.value, params);
|
||||
let { authList, buttonList } = result;
|
||||
let dataSource: Recordable[] = [];
|
||||
// concat 固定按钮
|
||||
// -update-begin--author:liaozhiyang---date:20240614---for:【TV360X-1017】子表权限管理按钮权限根据主表主题模版只显示所需按钮
|
||||
// 获取内置按钮
|
||||
const buttons = getButtonList(result);
|
||||
// -update-end--author:liaozhiyang---date:20240614---for:【TV360X-1017】子表权限管理按钮权限根据主表主题模版只显示所需按钮
|
||||
for (let btn of buttons) {
|
||||
|
||||
// 去除重复数据
|
||||
const findBtnIdx = buttonList.findIndex((item) => item.buttonCode === btn.code);
|
||||
const findBtn: Recordable = {}
|
||||
if (findBtnIdx !== -1) {
|
||||
findBtn.title = buttonList[findBtnIdx].buttonName;
|
||||
buttonList.splice(findBtnIdx, 1);
|
||||
}
|
||||
|
||||
let item = {
|
||||
status: 0,
|
||||
page: pageControlList.value,
|
||||
};
|
||||
let auth = authList.find((auth) => auth.code == btn.code);
|
||||
Object.assign(btn, item, auth, findBtn);
|
||||
dataSource.push(btn);
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240520---for:【TV360X-187】去掉子表权限管理中按钮权限的高级查询
|
||||
if (props.tableType == 3) {
|
||||
const findIndex = dataSource.findIndex((item) => item.code === 'super_query');
|
||||
if (findIndex != -1) {
|
||||
dataSource.splice(findIndex, 1);
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240520---for:【TV360X-187】去掉子表权限管理中按钮权限的高级查询
|
||||
// update-begin--author:liaozhiyang---date:20250403---for:【QQYUN-11801】生成测试数据
|
||||
if ([2, 3].includes(+props.tableType)) {
|
||||
const findIndex = dataSource.findIndex((item) => item.code == 'aigc_mock_data');
|
||||
if (findIndex != -1) {
|
||||
dataSource.splice(findIndex, 1);
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20250403---for:【QQYUN-11801】生成测试数据
|
||||
// concat 查询的自定义按钮
|
||||
return concatCustomButton(authList, buttonList, dataSource);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function concatCustomButton(authList: any[], buttonList: any[], dataSource: any[]) {
|
||||
for (let btn of buttonList) {
|
||||
//update-begin-author:taoyan date:2022-5-25 for: VUEN-1103 自定义按钮,开启权限控制后,再打开未保存上
|
||||
let auth = authList.find((auth) => auth.code == btn.buttonCode);
|
||||
//update-end-author:taoyan date:2022-5-25 for: VUEN-1103 自定义按钮,开启权限控制后,再打开未保存上
|
||||
let item = {
|
||||
code: btn.buttonCode,
|
||||
title: btn.buttonName,
|
||||
status: 0,
|
||||
page: btn.buttonStyle == 'form' ? pageControlForm.value : pageControlList.value,
|
||||
};
|
||||
dataSource.push(Object.assign(item, auth));
|
||||
}
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
async function onUpdateStatus(flag, record) {
|
||||
flag ? doEnableAuthButton(record) : doDisableAuthButton(record);
|
||||
}
|
||||
|
||||
// 启用按钮权限
|
||||
async function doEnableAuthButton(record: Recordable) {
|
||||
let result = await authButtonEnable({
|
||||
id: record.id,
|
||||
code: record.code,
|
||||
page: record.page,
|
||||
cgformId: cgformId.value,
|
||||
type: pageType.value,
|
||||
control: 5,
|
||||
status: 1,
|
||||
});
|
||||
record.id = result.id;
|
||||
record.status = 1;
|
||||
}
|
||||
|
||||
// 禁用按钮权限
|
||||
async function doDisableAuthButton(record: Recordable) {
|
||||
await authButtonDisable(record.id);
|
||||
record.status = 0;
|
||||
}
|
||||
|
||||
return { registerTable, onUpdateStatus };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable @register="registerTable" :loading="loading">
|
||||
<template #tableTitle>
|
||||
<a-button @click="onAdd" type="primary" preIcon="ant-design:plus">新增</a-button>
|
||||
</template>
|
||||
|
||||
<template #switch="{ text, record }">
|
||||
<a-switch size="small" :checked="record.status === 1" @click="() => onUpdateStatus(record)" />
|
||||
</template>
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!-- 子表单 -->
|
||||
<BasicModal v-bind="formModalProps" @openChange="handleOpenChange">
|
||||
<a-spin :spinning="formModalProps.confirmLoading">
|
||||
<BasicForm @register="registerForm" />
|
||||
</a-spin>
|
||||
</BasicModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { watch, defineComponent, ref, reactive, nextTick } from 'vue';
|
||||
import { BasicTable, TableAction, ActionItem, useTable } from '/@/components/Table';
|
||||
import { BasicModal, useModal } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form';
|
||||
import { authDataDelete, authDataLoadData, authDataSaveOrUpdate, authDataUpdateStatus } from '../auth.api';
|
||||
import { authDataColumns, useAuthDataFormSchemas, USE_SQL_RULES } from '../auth.data';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AuthDataConfig',
|
||||
components: { BasicTable, TableAction, BasicModal, BasicForm },
|
||||
props: {
|
||||
cgformId: { type: String, required: true },
|
||||
authFields: { type: Array, required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const loading = ref(false);
|
||||
const [registerTable, { reload, setLoading }] = useTable({
|
||||
api: (params) => authDataLoadData(props.cgformId, params),
|
||||
rowKey: 'id',
|
||||
bordered: true,
|
||||
columns: authDataColumns,
|
||||
showIndexColumn: false,
|
||||
// 操作列
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
title: '操作',
|
||||
fixed: false,
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
});
|
||||
watch(loading, (l) => setLoading(l));
|
||||
const [registerModal, { openModal, closeModal }] = useModal();
|
||||
const formModalProps = reactive({
|
||||
title: '',
|
||||
width: 800,
|
||||
confirmLoading: false,
|
||||
onOk: onSubmit,
|
||||
onCancel: closeModal,
|
||||
onRegister: registerModal,
|
||||
});
|
||||
let isUpdate = false;
|
||||
let formRecord = {};
|
||||
let isManualEnter = false;
|
||||
const { formSchemas } = useAuthDataFormSchemas(props, {
|
||||
onRuleOperatorChange,
|
||||
onRuleColumnChange,
|
||||
onRuleNameChange,
|
||||
});
|
||||
// 表单配置
|
||||
const [registerForm, { validate, resetFields, setFieldsValue, getFieldsValue, clearValidate, updateSchema }] = useForm({
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
labelAlign: 'right',
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.cgformId,
|
||||
() => {
|
||||
reload().catch(() => null);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
async function openFormModal(data) {
|
||||
isUpdate = data.isUpdate ?? false;
|
||||
formModalProps.title = data.title;
|
||||
openModal();
|
||||
await nextTick();
|
||||
await resetFields();
|
||||
formRecord = Object.assign({}, data.record);
|
||||
await setFieldsValue(formRecord);
|
||||
}
|
||||
|
||||
function onAdd() {
|
||||
openFormModal({ title: '新增' });
|
||||
}
|
||||
|
||||
function onEdit(record) {
|
||||
openFormModal({ title: '编辑', record, isUpdate: true });
|
||||
}
|
||||
|
||||
function onDelete(id) {
|
||||
loading.value = true;
|
||||
authDataDelete(id)
|
||||
.then(reload)
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
formModalProps.confirmLoading = true;
|
||||
let formData = await validate();
|
||||
formData = Object.assign({}, formRecord, formData);
|
||||
if (formData.ruleOperator == USE_SQL_RULES) {
|
||||
formData.ruleColumn = '';
|
||||
}
|
||||
formData.cgformId = props.cgformId;
|
||||
await authDataSaveOrUpdate(formData, isUpdate);
|
||||
reload();
|
||||
closeModal();
|
||||
} finally {
|
||||
formModalProps.confirmLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onUpdateStatus(record) {
|
||||
loading.value = true;
|
||||
let status = Math.abs(record.status - 1);
|
||||
authDataUpdateStatus({ ...record, status })
|
||||
.then(() => {
|
||||
record.status = status;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function onRuleOperatorChange(val) {
|
||||
if (val == USE_SQL_RULES) {
|
||||
setFieldsValue({
|
||||
ruleColumn: '',
|
||||
ruleValue: '',
|
||||
});
|
||||
updateSchema({
|
||||
field: 'ruleValue',
|
||||
component: 'InputTextArea',
|
||||
});
|
||||
clearValidate(['ruleValue']);
|
||||
} else {
|
||||
updateSchema({
|
||||
field: 'ruleValue',
|
||||
component: 'JInputSelect',
|
||||
});
|
||||
}
|
||||
}
|
||||
// -update-begin--author:liaozhiyang---date:20240607---for:【TV360X-536】数据权限配置配置优化及新增JInputSelect组件
|
||||
function onRuleColumnChange(val) {
|
||||
const values = getFieldsValue();
|
||||
if (!values.ruleName || (values.ruleName && !isManualEnter)) {
|
||||
const findItem: any = props.authFields.find((item: any) => item.value === val);
|
||||
const text = findItem ? findItem.text : val;
|
||||
setFieldsValue({
|
||||
ruleName: text,
|
||||
});
|
||||
}
|
||||
}
|
||||
function onRuleNameChange(e) {
|
||||
if (e.target.value.length) {
|
||||
isManualEnter = true;
|
||||
} else {
|
||||
isManualEnter = false;
|
||||
}
|
||||
}
|
||||
function handleOpenChange(visible) {
|
||||
if (visible) {
|
||||
isManualEnter = false;
|
||||
}
|
||||
}
|
||||
// -update-end--author:liaozhiyang---date:20240607---for:【TV360X-536】数据权限配置配置优化及新增JInputSelect组件
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: () => onEdit(record),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
placement: 'left',
|
||||
confirm: () => onDelete(record.id),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return {
|
||||
loading,
|
||||
formModalProps,
|
||||
onAdd,
|
||||
onUpdateStatus,
|
||||
getTableAction,
|
||||
getDropDownAction,
|
||||
registerTable,
|
||||
registerModal,
|
||||
registerForm,
|
||||
handleOpenChange,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,364 @@
|
||||
<template>
|
||||
<div class="auth-field-config">
|
||||
<BasicTable @register="registerTable" @change="handleTableChange" :loading="tableLoading">
|
||||
<!-- update-begin--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选 -->
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.dataIndex === 'switch'">
|
||||
<a-switch :loading="allSloading" v-model:checked="allSwitch" size="small" @change="handleChangeSwitch"></a-switch>启用
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'list'">
|
||||
<a-checkbox :indeterminate="listIndeterminate" v-model:checked="allListControl" :disabled="!allSwitch" @change="handleChangeList">{{ column.customTitle }}</a-checkbox>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'form'">
|
||||
<a-checkbox :indeterminate="formIndeterminate" v-model:checked="allFormControl" :disabled="!allSwitch" @change="handleChangeForm">{{ column.customTitle }}</a-checkbox>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ column.customTitle }}
|
||||
</template>
|
||||
</template>
|
||||
<!-- update-end--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选 -->
|
||||
<template #switch="{ text, record }">
|
||||
<a-switch size="small" :checked="record.status === 1" @change="(flag) => onUpdateStatus(flag, record)" />
|
||||
</template>
|
||||
|
||||
<template #list="{ text, record }">
|
||||
<a-checkbox :checked="record.listShow" :disabled="record.status === 0" @change="(e) => onCheckboxChange(e, record, 1)"> 可见 </a-checkbox>
|
||||
</template>
|
||||
|
||||
<template #form="{ text, record }">
|
||||
<a-checkbox :checked="record.formShow" :disabled="record.status === 0" @change="(e) => onCheckboxChange(e, record, 2)"> 可见 </a-checkbox>
|
||||
<a-checkbox :checked="record.formEditable" :disabled="record.status === 0" @change="(e) => onCheckboxChange(e, record, 3)">
|
||||
可编辑
|
||||
</a-checkbox>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { watch, defineComponent, ref } from 'vue';
|
||||
import { BasicTable, useTable } from '/@/components/Table';
|
||||
import { authFieldLoadData, authFieldUpdateCheckbox, authFieldUpdateStatus, batchAuthFieldUpdateStatus, batchAuthFieldUpdateCheckbox } from '../auth.api';
|
||||
import { authFieldColumns } from '../auth.data';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AuthFieldConfig',
|
||||
components: { BasicTable },
|
||||
props: {
|
||||
headId: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ['update:authFields'],
|
||||
setup(props, { emit }) {
|
||||
const cgformId = ref('');
|
||||
const [registerTable, { reload, getTableRef, setPagination }] = useTable({
|
||||
api: loadData,
|
||||
rowKey: 'code',
|
||||
bordered: true,
|
||||
columns: authFieldColumns,
|
||||
showIndexColumn: false,
|
||||
});
|
||||
const allSwitch = ref(false);
|
||||
const allListControl = ref(false);
|
||||
const allFormControl = ref(false);
|
||||
const allSloading = ref(false);
|
||||
const tableLoading = ref(false);
|
||||
const formIndeterminate = ref(false);
|
||||
const listIndeterminate = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.headId,
|
||||
(headId) => {
|
||||
cgformId.value = headId.split('?')[0];
|
||||
// update-begin--author:liaozhiyang---date:20240520---for:【TV360X-149】点击权限控制进入页面后,分页没有重置
|
||||
getTableRef().value && setPagination({ current: 1, pageSize: 10 });
|
||||
// update-end--author:liaozhiyang---date:20240520---for:【TV360X-149】点击权限控制进入页面后,分页没有重置
|
||||
reload().catch(() => null);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 加载数据
|
||||
async function loadData(params) {
|
||||
const exclude = ['id'];
|
||||
let data = await authFieldLoadData(cgformId.value, params);
|
||||
let fields: any[] = [];
|
||||
let filterData: any[] = [];
|
||||
data.forEach((item) => {
|
||||
if (exclude.indexOf(item.code) < 0) {
|
||||
if (item.isShowForm == 1 || item.isShowList == 1) {
|
||||
filterData.push(item);
|
||||
}
|
||||
//update-begin-author:taoyan date:2022-8-9 for: VUEN-1957 【online】同步数据库新增字段未提交
|
||||
if(item.dbIsPersist==1){
|
||||
fields.push({
|
||||
text: item.title,
|
||||
value: item.code,
|
||||
// -update-begin--author:liaozhiyang---date:20240617---for:【TV360X-201】权限管理条件根据控件过滤
|
||||
view: item.fieldShowType,
|
||||
dbType: item.dbType,
|
||||
// -update-end--author:liaozhiyang---date:20240617---for:【TV360X-201】权限管理条件根据控件过滤
|
||||
});
|
||||
}
|
||||
//update-end-author:taoyan date:2022-8-9 for: VUEN-1957 【online】同步数据库新增字段未提交
|
||||
}
|
||||
});
|
||||
|
||||
emit('update:authFields', fields);
|
||||
// update-begin--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选
|
||||
setCurDataStatus(params.pageNo, params.pageSize, filterData);
|
||||
// update-end--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选
|
||||
return filterData;
|
||||
}
|
||||
|
||||
async function onUpdateStatus(flag, record) {
|
||||
await authFieldUpdateStatus({
|
||||
cgformId: cgformId.value,
|
||||
code: record.code,
|
||||
status: flag ? 1 : 0,
|
||||
});
|
||||
if (!(record.formEditable || record.formShow || record.listShow)) {
|
||||
record.formEditable = true;
|
||||
record.formShow = true;
|
||||
record.listShow = true;
|
||||
}
|
||||
record.status = Math.abs(record.status - 1);
|
||||
// update-begin--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选
|
||||
itemChange();
|
||||
// update-end--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选
|
||||
}
|
||||
|
||||
async function onCheckboxChange(event, record, switchFlag) {
|
||||
let checked = event.target.checked;
|
||||
await authFieldUpdateCheckbox({
|
||||
cgformId: cgformId.value,
|
||||
code: record.code,
|
||||
switchFlag: switchFlag,
|
||||
listShow: checked,
|
||||
formShow: checked,
|
||||
formEditable: checked,
|
||||
});
|
||||
if (switchFlag == 1) {
|
||||
record.listShow = checked;
|
||||
} else if (switchFlag == 2) {
|
||||
record.formShow = checked;
|
||||
} else if (switchFlag == 3) {
|
||||
record.formEditable = checked;
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240807---for:【TV360X-2087】依次去掉当前行的列表控制可见、表单控制可见、可编辑时启动状态没变
|
||||
if (record.listShow === false && record.formShow === false && record.formEditable === false) {
|
||||
record.status = 0;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240807---for:【TV360X-2087】依次去掉当前行的列表控制可见、表单控制可见、可编辑时启动状态没变
|
||||
// update-begin--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选
|
||||
itemChange();
|
||||
// update-end--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选
|
||||
// 加载每一页数据时设置状态
|
||||
function setCurDataStatus(current, pageSize, data) {
|
||||
const result: any = [];
|
||||
if (data?.length) {
|
||||
const max = current * pageSize > data.length ? data.length : current * pageSize;
|
||||
for (let i = current * pageSize - pageSize; i < max; i++) {
|
||||
const item = data[i];
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
if (result.length) {
|
||||
// 默认都是true,只有一项为false则总开关即是false
|
||||
allSwitch.value = true;
|
||||
allListControl.value = true;
|
||||
allFormControl.value = true;
|
||||
result.forEach((item) => {
|
||||
if (allSwitch.value && item.status == 0) {
|
||||
allSwitch.value = false;
|
||||
}
|
||||
if (allListControl.value && item.listShow == false) {
|
||||
allListControl.value = false;
|
||||
}
|
||||
if (allFormControl.value && (item.formEditable == false || item.formShow == false)) {
|
||||
allFormControl.value = false;
|
||||
}
|
||||
});
|
||||
if (allListControl.value == true) {
|
||||
listIndeterminate.value = false;
|
||||
} else {
|
||||
const findItem = result.find((item) => item.listShow);
|
||||
if (findItem) {
|
||||
listIndeterminate.value = true;
|
||||
} else {
|
||||
listIndeterminate.value = false;
|
||||
}
|
||||
}
|
||||
if (allFormControl.value == true) {
|
||||
formIndeterminate.value = false;
|
||||
} else {
|
||||
const findItem = result.find((item) => item.formEditable || item.formShow);
|
||||
if (findItem) {
|
||||
formIndeterminate.value = true;
|
||||
} else {
|
||||
formIndeterminate.value = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
allSwitch.value = false;
|
||||
allListControl.value = false;
|
||||
allFormControl.value = false;
|
||||
}
|
||||
}
|
||||
// 单独项状态变动时
|
||||
const itemChange = () => {
|
||||
const { current, pageSize } = getTableRef().value!.getPaginationRef();
|
||||
const allDataSource = getTableRef().value!.getDataSource();
|
||||
setCurDataStatus(current, pageSize, allDataSource);
|
||||
};
|
||||
|
||||
// 获取某页dataSource
|
||||
const getCurrentDataSource = (current, pageSize) => {
|
||||
const result: any = [];
|
||||
const allDataSource = getTableRef().value!.getDataSource();
|
||||
if (allDataSource?.length) {
|
||||
const max = current * pageSize > allDataSource.length ? allDataSource.length :current * pageSize;
|
||||
for (let i = current * pageSize - pageSize; i < max; i++) {
|
||||
const item = allDataSource[i];
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
// 开关(表头|总的)
|
||||
const handleChangeSwitch = async (checked) => {
|
||||
tableLoading.value = true;
|
||||
allSwitch.value = checked;
|
||||
const { current, pageSize } = getTableRef().value!.getPaginationRef();
|
||||
const dataSource = getCurrentDataSource(current, pageSize);
|
||||
let params = dataSource.map((item) => ({ cgformId: item.cgformId, code: item.code, status: checked ? 1 : 0 }));
|
||||
allSloading.value = true;
|
||||
await batchAuthFieldUpdateStatus(params);
|
||||
dataSource.forEach((item) => {
|
||||
if (checked) {
|
||||
item.status = 1;
|
||||
} else {
|
||||
item.status = 0;
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240807---for:【TV360X-1992】数据权限-字段权限全选启用,列表控制和表单控制勾选
|
||||
if (!(item.formEditable || item.formShow || item.listShow)) {
|
||||
item.formEditable = true;
|
||||
item.formShow = true;
|
||||
item.listShow = true;
|
||||
}
|
||||
});
|
||||
// update-end--author:liaozhiyang---date:20240807---for:【TV360X-1992】数据权限-字段权限全选启用,列表控制和表单控制勾选
|
||||
allSloading.value = false;
|
||||
tableLoading.value = false;
|
||||
const allDataSource = getTableRef().value!.getDataSource();
|
||||
setCurDataStatus(current, pageSize, allDataSource);
|
||||
};
|
||||
const handleChangeList = async (e) => {
|
||||
tableLoading.value = true;
|
||||
const checked = e.target.checked;
|
||||
allListControl.value = checked;
|
||||
const { current, pageSize } = getTableRef().value!.getPaginationRef();
|
||||
const dataSource = getCurrentDataSource(current, pageSize);
|
||||
let params = dataSource.map((item) => ({ cgformId: item.cgformId, code: item.code, switchFlag: 1, listShow: !!checked }));
|
||||
await batchAuthFieldUpdateCheckbox(params);
|
||||
dataSource.forEach((item) => {
|
||||
item.listShow = !!checked;
|
||||
// update-begin--author:liaozhiyang---date:20240807---for:【TV360X-2087】依次去掉当前行的列表控制可见、表单控制可见、可编辑时启动状态没变
|
||||
if (item.listShow === false && item.formShow === false && item.formEditable === false) {
|
||||
item.status = 0;
|
||||
allSwitch.value = false;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240807---for:【TV360X-2087】依次去掉当前行的列表控制可见、表单控制可见、可编辑时启动状态没变
|
||||
});
|
||||
if (checked) {
|
||||
listIndeterminate.value = false;
|
||||
}
|
||||
tableLoading.value = false;
|
||||
};
|
||||
const handleChangeForm = async (e) => {
|
||||
tableLoading.value = true;
|
||||
const checked = e.target.checked;
|
||||
allFormControl.value = checked;
|
||||
const { current, pageSize } = getTableRef().value!.getPaginationRef();
|
||||
const dataSource = getCurrentDataSource(current, pageSize);
|
||||
const params = [
|
||||
...dataSource.map((item) => ({ cgformId: item.cgformId, code: item.code, switchFlag: 4, formShow: !!checked, formEditable: !!checked })),
|
||||
];
|
||||
await batchAuthFieldUpdateCheckbox(params);
|
||||
dataSource.forEach((item) => {
|
||||
item.formEditable = !!checked;
|
||||
item.formShow = !!checked;
|
||||
// update-begin--author:liaozhiyang---date:20240807---for:【TV360X-2087】依次去掉当前行的列表控制可见、表单控制可见、可编辑时启动状态没变
|
||||
if (item.listShow === false && item.formShow === false && item.formEditable === false) {
|
||||
item.status = 0;
|
||||
allSwitch.value = false;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240807---for:【TV360X-2087】依次去掉当前行的列表控制可见、表单控制可见、可编辑时启动状态没变
|
||||
});
|
||||
if (checked) {
|
||||
formIndeterminate.value = false;
|
||||
}
|
||||
tableLoading.value = false;
|
||||
};
|
||||
// 监听页码变动
|
||||
const handleTableChange = (pagination) => {
|
||||
// const { current, pageSize } = pagination;
|
||||
// const dataSource = getCurrentDataSource(current, pageSize);
|
||||
// // 先置初始值
|
||||
// allSwitch.value = false;
|
||||
// if(dataSource.length) {
|
||||
|
||||
// }
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240612---for:【TV360X-148】权限配置加全选
|
||||
return { registerTable, onUpdateStatus, onCheckboxChange, handleChangeSwitch, allSwitch, allFormControl, allListControl, allSloading, handleTableChange, handleChangeList, handleChangeForm, tableLoading, formIndeterminate, listIndeterminate, };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.auth-field-config :deep(.ant-checkbox + span) {
|
||||
padding-left: 2px;
|
||||
}
|
||||
:deep(.ant-table-thead) {
|
||||
.ant-switch {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.ant-checkbox-wrapper {
|
||||
.ant-checkbox {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.ant-checkbox-disabled + span {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
html[data-theme='light'] {
|
||||
.auth-field-config {
|
||||
:deep(.ant-table-thead) {
|
||||
.ant-checkbox-wrapper {
|
||||
.ant-checkbox-disabled + span {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
html[data-theme='dark'] {
|
||||
.auth-field-config {
|
||||
:deep(.ant-table-thead) {
|
||||
.ant-checkbox-wrapper {
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
.ant-checkbox-disabled + span {
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-empty v-if="disabled" description="请先选中左侧角色/部门/用户" />
|
||||
<a-empty v-else-if="treeData.length === 0" description="无权限信息" />
|
||||
<template v-else>
|
||||
<div class="onl-auth-tree-btns">
|
||||
<a-button @click="onRefresh" size="small" type="primary" preIcon="ant-design:redo" ghost>刷新</a-button>
|
||||
<a-button @click="onSave" size="small" type="primary" preIcon="ant-design:save" ghost>保存</a-button>
|
||||
</div>
|
||||
<a-tree
|
||||
checkable
|
||||
v-model:checkedKeys="checkedKeys"
|
||||
:expandedKeys="expandedKeys"
|
||||
:autoExpandParent="autoExpandParent"
|
||||
:treeData="treeData"
|
||||
@expand="onExpand"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch, computed } from 'vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { authButtonFixedList } from '../auth.data';
|
||||
import { authButtonLoadTree, loadRoleAuthChecked, saveAuthButton } from '../auth.api';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AuthButtonTree',
|
||||
props: {
|
||||
cgformId: { type: String, required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const { createMessage: $message, createSuccessModal } = useMessage();
|
||||
const roleId = ref('');
|
||||
const authType = ref(2);
|
||||
const autoExpandParent = ref(true);
|
||||
const expandedKeys = ref<string[]>([]);
|
||||
const checkedKeys = ref<string[]>([]);
|
||||
|
||||
const treeData = ref<Recordable[]>([]);
|
||||
const authMode = ref('');
|
||||
const disabled = computed(() => !roleId.value);
|
||||
watch(() => props.cgformId, loadTree, { immediate: true });
|
||||
|
||||
async function loadTree() {
|
||||
if (!props.cgformId) return;
|
||||
let result = (await authButtonLoadTree(props.cgformId, authType.value)) as Recordable[];
|
||||
//1.遍历第一次 根据code设置默认按钮名称
|
||||
result.forEach((item) => {
|
||||
for (const btn of authButtonFixedList) {
|
||||
if (item.code == btn.code) {
|
||||
if (!item['title']) {
|
||||
item['title'] = btn.title;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
//2.拼树形数据
|
||||
let trees: Recordable[] = [];
|
||||
for (let item of result) {
|
||||
let title = getTreeNodeTitle(item);
|
||||
trees.push({ key: item.id, title });
|
||||
}
|
||||
treeData.value = trees;
|
||||
}
|
||||
|
||||
function onRefresh() {
|
||||
loadTree();
|
||||
loadChecked(roleId.value, authMode.value);
|
||||
}
|
||||
|
||||
// 给外部调用,加载当前选择的权限
|
||||
async function loadChecked($roleId, $authMode) {
|
||||
roleId.value = $roleId;
|
||||
authMode.value = $authMode;
|
||||
checkedKeys.value = [];
|
||||
await loadTree();
|
||||
let result = (await loadRoleAuthChecked({
|
||||
roleId: $roleId,
|
||||
cgformId: props.cgformId,
|
||||
type: authType.value,
|
||||
authMode: $authMode,
|
||||
})) as Recordable[];
|
||||
checkedKeys.value = result.map((item) => item.authId);
|
||||
}
|
||||
|
||||
function clearChecked() {
|
||||
roleId.value = '';
|
||||
// QQYUN-4284 【online表单】权限管理 开启按钮后,在角色授权中显示,当关闭时,再打开角色权限仍然显示,需刷新页面才不显示
|
||||
loadTree();
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
try {
|
||||
const {success, message, result} = await saveAuthButton(roleId.value, props.cgformId, {
|
||||
authId: JSON.stringify(checkedKeys.value),
|
||||
authMode: authMode.value,
|
||||
});
|
||||
if (success) {
|
||||
if (Array.isArray(result?.disabledNames)) {
|
||||
createSuccessModal({
|
||||
title: '保存成功',
|
||||
content: `由于以下按钮未激活,所以权限未生效。<br>${result.disabledNames.join('<br>')}`,
|
||||
});
|
||||
} else {
|
||||
$message.success('保存成功');
|
||||
}
|
||||
} else {
|
||||
$message.error(message);
|
||||
}
|
||||
} catch (e) {
|
||||
$message.error('保存出现异常');
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function getTreeNodeTitle(item) {
|
||||
let str = item.title + '-';
|
||||
if (item.code && item.code.includes('form_sub')) {
|
||||
// 【TV360X-2711】当 code 以 form_sub 开头时,说明是附表
|
||||
str += '表单可见(附表)'
|
||||
} else if (item.page == 3) {
|
||||
str += '列表可见';
|
||||
} else if (item.page == 5) {
|
||||
str += '表单可见';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function onExpand($expandedKeys) {
|
||||
expandedKeys.value = $expandedKeys;
|
||||
autoExpandParent.value = false;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
roleId.value = '';
|
||||
checkedKeys.value = [];
|
||||
}
|
||||
|
||||
return {
|
||||
loadChecked,
|
||||
clear,
|
||||
expandedKeys,
|
||||
autoExpandParent,
|
||||
checkedKeys,
|
||||
treeData,
|
||||
disabled,
|
||||
onSave,
|
||||
onExpand,
|
||||
onRefresh,
|
||||
clearChecked,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.onl-auth-tree-btns {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.onl-auth-tree-btns button {
|
||||
margin: 0 5px 0 2px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-empty v-if="disabled" description="请先选中左侧角色/部门/用户" />
|
||||
<a-empty v-else-if="treeData.length === 0" description="无权限信息" />
|
||||
<template v-else>
|
||||
<div class="onl-auth-tree-btns">
|
||||
<a-button @click="onRefresh" size="small" type="primary" preIcon="ant-design:redo" ghost>刷新</a-button>
|
||||
<a-button @click="onSave" size="small" type="primary" preIcon="ant-design:save" ghost>保存</a-button>
|
||||
</div>
|
||||
<a-tree
|
||||
checkable
|
||||
v-model:checkedKeys="checkedKeys"
|
||||
:expandedKeys="expandedKeys"
|
||||
:autoExpandParent="autoExpandParent"
|
||||
:treeData="treeData"
|
||||
@expand="onExpand"
|
||||
>
|
||||
</a-tree>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref, watch } from 'vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { authDataLoadTree, loadRoleAuthChecked, saveAuthData } from '../auth.api';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AuthDataTree',
|
||||
props: {
|
||||
cgformId: { type: String, required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const { createMessage: $message } = useMessage();
|
||||
const roleId = ref('');
|
||||
const authType = ref(3);
|
||||
const autoExpandParent = ref(true);
|
||||
const expandedKeys = ref<string[]>([]);
|
||||
const checkedKeys = ref<string[]>([]);
|
||||
const treeData = ref<Recordable[]>([]);
|
||||
const authMode = ref('');
|
||||
const disabled = computed(() => !roleId.value);
|
||||
watch(() => props.cgformId, loadTree, { immediate: true });
|
||||
|
||||
async function loadTree() {
|
||||
if (!props.cgformId) return;
|
||||
let result = (await authDataLoadTree(props.cgformId)) as Recordable[];
|
||||
treeData.value = result.map((item) => ({ key: item.id, title: item.ruleName }));
|
||||
}
|
||||
|
||||
async function loadChecked($roleId, $authMode) {
|
||||
roleId.value = $roleId;
|
||||
authMode.value = $authMode;
|
||||
checkedKeys.value = [];
|
||||
await loadTree();
|
||||
let result = (await loadRoleAuthChecked({
|
||||
roleId: $roleId,
|
||||
cgformId: props.cgformId,
|
||||
type: authType.value,
|
||||
authMode: $authMode,
|
||||
})) as Recordable[];
|
||||
checkedKeys.value = result.map((item) => item.authId);
|
||||
}
|
||||
|
||||
function clearChecked() {
|
||||
roleId.value = '';
|
||||
// QQYUN-4284 【online表单】权限管理 开启按钮后,在角色授权中显示,当关闭时,再打开角色权限仍然显示,需刷新页面才不显示
|
||||
loadTree();
|
||||
}
|
||||
|
||||
function onRefresh() {
|
||||
loadTree();
|
||||
loadChecked(roleId.value, authMode.value);
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
await saveAuthData(roleId.value, props.cgformId, {
|
||||
authId: JSON.stringify(checkedKeys.value),
|
||||
authMode: authMode.value,
|
||||
});
|
||||
$message.success('保存成功');
|
||||
}
|
||||
|
||||
function onExpand($expandedKeys) {
|
||||
expandedKeys.value = $expandedKeys;
|
||||
autoExpandParent.value = false;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
roleId.value = '';
|
||||
checkedKeys.value = [];
|
||||
}
|
||||
|
||||
return {
|
||||
loadChecked,
|
||||
clear,
|
||||
expandedKeys,
|
||||
autoExpandParent,
|
||||
checkedKeys,
|
||||
treeData,
|
||||
disabled,
|
||||
onSave,
|
||||
onExpand,
|
||||
onRefresh,
|
||||
clearChecked,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.onl-auth-tree-btns {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.onl-auth-tree-btns button {
|
||||
margin: 0 5px 0 2px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-empty v-if="disabled" description="请先选中左侧角色/部门/用户" />
|
||||
<a-empty v-else-if="treeData.length === 0" description="无权限信息" />
|
||||
<template v-else>
|
||||
<div class="onl-auth-tree-btns">
|
||||
<a-button @click="onRefresh" size="small" type="primary" preIcon="ant-design:redo" ghost>刷新</a-button>
|
||||
<a-button @click="onExpandAll" size="small" type="primary" ghost><DownCircleOutlined />展开</a-button>
|
||||
<a-button @click="onCloseAll" size="small" type="primary" ghost><UpCircleOutlined />折叠</a-button>
|
||||
<a-button @click="onSave" size="small" type="primary" preIcon="ant-design:save" ghost>保存</a-button>
|
||||
|
||||
<!-- update-begin-author:taoyan date:2022-5-25 for: VUEN-1102 字段授权 全选没有 -->
|
||||
<a-button @click="onSelectAll" size="small" type="primary" ghost><CheckOutlined />全选</a-button>
|
||||
<a-button @click="onClearSelected" size="small" type="primary" ghost><UndoOutlined />重置</a-button>
|
||||
<!-- update-end-author:taoyan date:2022-5-25 for: VUEN-1102 字段授权 全选没有 -->
|
||||
</div>
|
||||
<a-tree
|
||||
checkable
|
||||
v-model:checkedKeys="checkedKeys"
|
||||
:expandedKeys="expandedKeys"
|
||||
:autoExpandParent="autoExpandParent"
|
||||
:treeData="treeData"
|
||||
@expand="onExpand"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch, computed, unref } from 'vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { authFieldLoadTree, loadRoleAuthChecked, saveAuthField } from '../auth.api';
|
||||
import { DownCircleOutlined, HomeOutlined, UpCircleOutlined, CheckOutlined, UndoOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AuthFieldTree',
|
||||
components: {
|
||||
DownCircleOutlined,
|
||||
HomeOutlined,
|
||||
UpCircleOutlined,
|
||||
UndoOutlined,
|
||||
CheckOutlined,
|
||||
},
|
||||
props: {
|
||||
cgformId: { type: String, required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const { createMessage: $message } = useMessage();
|
||||
const roleId = ref('');
|
||||
const authType = ref(1);
|
||||
const autoExpandParent = ref(true);
|
||||
const expandedKeys = ref<string[]>([]);
|
||||
const checkedKeys = ref<string[]>([]);
|
||||
const allCode = ref<string[]>([]);
|
||||
const treeData = ref<Recordable[]>([]);
|
||||
const authMode = ref('');
|
||||
const disabled = computed(() => !roleId.value);
|
||||
watch(() => props.cgformId, loadTree, { immediate: true });
|
||||
|
||||
async function loadTree() {
|
||||
if (!props.cgformId) return;
|
||||
let result = (await authFieldLoadTree(props.cgformId, authType.value)) as Recordable[];
|
||||
// 1. 根据code找一级节点
|
||||
let trees: Recordable[] = [];
|
||||
let codes: string[] = [];
|
||||
result.forEach((item) => {
|
||||
if (!codes.includes(item.code)) {
|
||||
codes.push(item.code);
|
||||
trees.push({ key: item.code, title: item.title });
|
||||
}
|
||||
});
|
||||
// 2.双重遍历,拼接树形数据
|
||||
for (let node of trees) {
|
||||
let children: Recordable[] = [];
|
||||
for (let item of result) {
|
||||
if (node.key === item.code) {
|
||||
let temp = getTreeNodeTitle(item);
|
||||
children.push({ key: item.id, title: temp });
|
||||
}
|
||||
}
|
||||
node.children = children;
|
||||
}
|
||||
treeData.value = trees;
|
||||
expandedKeys.value = [...codes];
|
||||
allCode.value = codes;
|
||||
}
|
||||
|
||||
function getTreeNodeTitle(item) {
|
||||
let str = '';
|
||||
if (item.page == 3) {
|
||||
str += '列表';
|
||||
} else if (item.page == 5) {
|
||||
str += '表单';
|
||||
}
|
||||
if (item.control == 3) {
|
||||
str += '可编辑';
|
||||
} else if (item.control == 5) {
|
||||
str += '可见';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// 给外部调用,加载当前选择的权限
|
||||
async function loadChecked($roleId, $authMode) {
|
||||
roleId.value = $roleId;
|
||||
authMode.value = $authMode;
|
||||
checkedKeys.value = [];
|
||||
await loadTree();
|
||||
let result = (await loadRoleAuthChecked({
|
||||
roleId: $roleId,
|
||||
cgformId: props.cgformId,
|
||||
type: authType.value,
|
||||
authMode: $authMode,
|
||||
})) as Recordable[];
|
||||
checkedKeys.value = result.map((item) => item.authId);
|
||||
}
|
||||
|
||||
function clearChecked() {
|
||||
roleId.value = '';
|
||||
// QQYUN-4284 【online表单】权限管理 开启按钮后,在角色授权中显示,当关闭时,再打开角色权限仍然显示,需刷新页面才不显示
|
||||
loadTree();
|
||||
}
|
||||
|
||||
function onRefresh() {
|
||||
loadTree();
|
||||
loadChecked(roleId.value, authMode.value);
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
let ids = checkedKeys.value.filter((i) => allCode.value.indexOf(i) < 0);
|
||||
await saveAuthField(roleId.value, props.cgformId, {
|
||||
authId: JSON.stringify(ids),
|
||||
authMode: authMode.value,
|
||||
});
|
||||
$message.success('保存成功');
|
||||
}
|
||||
|
||||
function onExpandAll() {
|
||||
expandedKeys.value = [...allCode.value];
|
||||
}
|
||||
|
||||
function onCloseAll() {
|
||||
expandedKeys.value = [];
|
||||
}
|
||||
|
||||
function onExpand($expandedKeys) {
|
||||
expandedKeys.value = $expandedKeys;
|
||||
autoExpandParent.value = false;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
roleId.value = '';
|
||||
checkedKeys.value = [];
|
||||
}
|
||||
|
||||
// update-begin-author:taoyan date:2022-5-25 for: VUEN-1102 字段授权 全选没有
|
||||
// 取消选中--重置
|
||||
function onClearSelected() {
|
||||
checkedKeys.value = [];
|
||||
}
|
||||
// 全选
|
||||
function onSelectAll() {
|
||||
const selectFun = function (arr) {
|
||||
for (let node of arr) {
|
||||
checkedKeys.value.push(node.key);
|
||||
if (node.children && node.children.length > 0) {
|
||||
selectFun.call(null, node.children);
|
||||
}
|
||||
}
|
||||
};
|
||||
checkedKeys.value = [];
|
||||
selectFun.call(null, unref(treeData));
|
||||
}
|
||||
// update-end-author:taoyan date:2022-5-25 for: VUEN-1102 字段授权 全选没有
|
||||
|
||||
return {
|
||||
loadChecked,
|
||||
clear,
|
||||
expandedKeys,
|
||||
autoExpandParent,
|
||||
checkedKeys,
|
||||
treeData,
|
||||
disabled,
|
||||
onSave,
|
||||
onExpand,
|
||||
clearChecked,
|
||||
onCloseAll,
|
||||
onExpandAll,
|
||||
onRefresh,
|
||||
onClearSelected,
|
||||
onSelectAll,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.onl-auth-tree-btns {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.onl-auth-tree-btns button {
|
||||
margin: 0 5px 0 2px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-tree
|
||||
v-if="treeData.length > 0"
|
||||
showIcon
|
||||
autoExpandParent
|
||||
:treeData="treeData"
|
||||
:selectedKeys="selectedKeys"
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
@select="onSelect"
|
||||
>
|
||||
<template #icon="{ selected }">
|
||||
<a-icon :style="{ color: selected ? 'blue' : '' }" type="apartment" />
|
||||
</template>
|
||||
</a-tree>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, defineComponent } from 'vue';
|
||||
import { queryTreeList } from '/@/api/common/api';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LeftDepart',
|
||||
emits: ['select'],
|
||||
setup(_, { emit }) {
|
||||
const treeData = ref<Recordable[]>([]);
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const expandedKeys = ref<string[]>([]);
|
||||
|
||||
function onSelect(_, e) {
|
||||
let record = e.node.dataRef;
|
||||
selectedKeys.value = [record.key];
|
||||
emit('select', record.id);
|
||||
}
|
||||
|
||||
loadTree();
|
||||
|
||||
async function loadTree() {
|
||||
let result = await queryTreeList();
|
||||
treeData.value = [];
|
||||
result.forEach((node) => initialNode(node));
|
||||
}
|
||||
|
||||
function initialNode(node, level = 1) {
|
||||
if (level === 1) {
|
||||
treeData.value.push(node);
|
||||
expandedKeys.value.push(node.id);
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
for (const childNode of node.children) {
|
||||
initialNode(childNode, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearSelected() {
|
||||
selectedKeys.value = [];
|
||||
}
|
||||
|
||||
return {
|
||||
treeData,
|
||||
expandedKeys,
|
||||
selectedKeys,
|
||||
clearSelected,
|
||||
onSelect,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #tableTop><span></span></template>
|
||||
</BasicTable>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { BasicTable } from '/@/components/Table';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LeftRole',
|
||||
components: { BasicTable },
|
||||
emits: ['select'],
|
||||
setup(_, { emit }) {
|
||||
const { tableContext, createMessage: $message } = useListPage({
|
||||
tableProps: {
|
||||
api: loadData,
|
||||
rowKey: 'id',
|
||||
size: 'small',
|
||||
bordered: true,
|
||||
columns: [
|
||||
{ title: '角色编码', align: 'center', dataIndex: 'roleCode' },
|
||||
{ title: '角色名称', align: 'center', dataIndex: 'roleName' },
|
||||
],
|
||||
rowSelection: {
|
||||
type: 'radio',
|
||||
onChange(selectedRowKeys) {
|
||||
if (selectedRowKeys.length > 0) {
|
||||
emit('select', selectedRowKeys[0]);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
canResize: false,
|
||||
clickToRowSelect: true,
|
||||
useSearchForm: false,
|
||||
showActionColumn: false,
|
||||
showTableSetting: false,
|
||||
},
|
||||
});
|
||||
const [registerTable, { clearSelectedRowKeys }, { rowSelection }] = tableContext;
|
||||
|
||||
async function loadData(params) {
|
||||
let { code, success, result, message } = await defHttp.get(
|
||||
{
|
||||
url: '/sys/role/list',
|
||||
params,
|
||||
},
|
||||
{ isTransformResponse: false }
|
||||
);
|
||||
if (success) {
|
||||
return result;
|
||||
}
|
||||
if (code === 510) {
|
||||
$message.warning(message);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function clearSelected() {
|
||||
clearSelectedRowKeys();
|
||||
}
|
||||
|
||||
return { rowSelection, registerTable, clearSelected };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #tableTop><span></span></template>
|
||||
</BasicTable>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { BasicTable } from '/@/components/Table';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LeftUser',
|
||||
components: { BasicTable },
|
||||
emits: ['select'],
|
||||
setup(_, { emit }) {
|
||||
const { tableContext, createMessage: $message } = useListPage({
|
||||
tableProps: {
|
||||
api: loadData,
|
||||
rowKey: 'id',
|
||||
size: 'small',
|
||||
bordered: true,
|
||||
columns: [
|
||||
{ title: '账号', dataIndex: 'username', width: 200 },
|
||||
{ title: '姓名', dataIndex: 'realname', width: 200 },
|
||||
],
|
||||
rowSelection: {
|
||||
type: 'radio',
|
||||
onChange(selectedRowKeys) {
|
||||
if (selectedRowKeys.length > 0) {
|
||||
emit('select', selectedRowKeys[0]);
|
||||
}
|
||||
},
|
||||
},
|
||||
formConfig: {
|
||||
schemas: [
|
||||
{
|
||||
label: '账号',
|
||||
field: 'username',
|
||||
component: 'JInput',
|
||||
componentProps: {
|
||||
placeholder: '输入账号',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
field: 'realname',
|
||||
component: 'JInput',
|
||||
componentProps: {
|
||||
placeholder: '输入姓名',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
canResize: false,
|
||||
clickToRowSelect: true,
|
||||
showActionColumn: false,
|
||||
showTableSetting: false,
|
||||
},
|
||||
});
|
||||
const [registerTable, { clearSelectedRowKeys }, { rowSelection }] = tableContext;
|
||||
|
||||
async function loadData(params) {
|
||||
let { code, success, result, message } = await defHttp.get(
|
||||
{
|
||||
url: '/sys/user/list',
|
||||
params,
|
||||
},
|
||||
{ isTransformResponse: false }
|
||||
);
|
||||
if (success) {
|
||||
return result;
|
||||
}
|
||||
if (code === 510) {
|
||||
$message.warning(message);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function clearSelected() {
|
||||
clearSelectedRowKeys();
|
||||
}
|
||||
|
||||
return { rowSelection, registerTable, clearSelected };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" title="内置按钮" :width="1200" @cancel="onCancel">
|
||||
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)"/>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<template #footer>
|
||||
<a-button @click="onCancel">关闭</a-button>
|
||||
</template>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<BasicModal v-bind="formModalProps">
|
||||
<a-spin :spinning="formModalProps.confirmLoading">
|
||||
<div style="margin-top: 20px;">
|
||||
<BasicForm @register="registerForm"/>
|
||||
</div>
|
||||
</a-spin>
|
||||
</BasicModal>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, nextTick, reactive, ref} from 'vue';
|
||||
import {useListPage} from '/@/hooks/system/useListPage';
|
||||
|
||||
import {BasicTable, TableAction} from '/@/components/Table';
|
||||
import {BasicModal, useModal, useModalInner} from '/@/components/Modal';
|
||||
import {BasicForm, useForm} from '/@/components/Form';
|
||||
import {builtInList, saveOrUpdate} from './button.api';
|
||||
import {columns, formSchemas} from './button.data';
|
||||
|
||||
const props = defineProps({
|
||||
record: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['register']);
|
||||
|
||||
const code = computed(() => props.record?.id);
|
||||
// 1单表、2主表、3附表
|
||||
const tableType = computed(() => props.record?.tableType);
|
||||
|
||||
const isSingleTable = computed(() => tableType.value === 1);
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const {tableContext} = useListPage({
|
||||
tableProps: {
|
||||
api: async (params) => {
|
||||
const list = await builtInList(code.value, params)
|
||||
if (isSingleTable.value) {
|
||||
// 过滤包含 sub_ 的按钮,说明是附表按钮,单表时不显示
|
||||
return list.filter(item => !item.buttonCode.includes('sub_'));
|
||||
}
|
||||
return list;
|
||||
},
|
||||
columns: columns.filter(item => !['buttonStyle', 'optType', 'orderNum', 'exp'].includes(item.dataIndex as string)),
|
||||
canResize: false,
|
||||
useSearchForm: false,
|
||||
pagination: false,
|
||||
},
|
||||
});
|
||||
// 注册table数据
|
||||
const [registerTable, {reload}, {rowSelection}] = tableContext;
|
||||
// 注册弹窗
|
||||
const [registerModal, {closeModal}] = useModalInner(() => reload());
|
||||
|
||||
// 注册 form 弹窗
|
||||
const [registerFormModal, formModal] = useModal();
|
||||
const isUpdate = ref(false);
|
||||
const formModalProps = reactive({
|
||||
onRegister: registerFormModal,
|
||||
title: computed(() => (isUpdate?.value ? '修改' : '新增')),
|
||||
width: 600,
|
||||
centered: true,
|
||||
confirmLoading: false,
|
||||
onOk: onSubmit,
|
||||
onCancel: formModal.closeModal,
|
||||
});
|
||||
let formRecord = {};
|
||||
|
||||
const schemas = [
|
||||
...formSchemas({redoModalHeight: formModal.redoModalHeight}),
|
||||
].filter((schema) => {
|
||||
return ['buttonCode', 'buttonName', 'buttonIcon', 'buttonStatus'].includes(schema.field);
|
||||
}).map(schema => {
|
||||
if ('buttonCode' === schema.field) {
|
||||
return {
|
||||
...schema,
|
||||
dynamicRules: () => [],
|
||||
dynamicDisabled: () => true,
|
||||
}
|
||||
} else if ('buttonIcon' === schema.field) {
|
||||
return {
|
||||
...schema,
|
||||
ifShow: () => true,
|
||||
// 以下按钮不允许设置图标
|
||||
dynamicDisabled: ({values}) => ['bpm', 'edit', 'detail', 'delete'].includes(values.buttonCode),
|
||||
}
|
||||
}
|
||||
return schema
|
||||
});
|
||||
|
||||
// 注册 form
|
||||
const [registerForm, {resetFields, setFieldsValue, validate}] = useForm({
|
||||
// update-begin--author:liaozhiyang---date:20240618---for:【TV360X-1306】自定义按钮弹窗按钮样式切换是重置弹窗高度
|
||||
schemas: schemas,
|
||||
// update-end--author:liaozhiyang---date:20240618---for:【TV360X-1306】自定义按钮弹窗按钮样式切换是重置弹窗高度
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
async function openFormModal(data: Recordable) {
|
||||
isUpdate.value = data.isUpdate;
|
||||
formRecord = {...(data.record ?? {})};
|
||||
formModal.openModal();
|
||||
await nextTick();
|
||||
await resetFields();
|
||||
setFieldsValue(formRecord);
|
||||
}
|
||||
|
||||
// 编辑按钮
|
||||
function onEdit(record: Recordable) {
|
||||
openFormModal({isUpdate: true, record});
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
formModalProps.confirmLoading = true;
|
||||
let values = await validate();
|
||||
values = Object.assign({cgformHeadId: code.value}, formRecord, values);
|
||||
|
||||
console.log('onSubmit - values :', values)
|
||||
const isUpdate = values.id != null
|
||||
|
||||
await saveOrUpdate(values, isUpdate);
|
||||
reload();
|
||||
formModal.closeModal();
|
||||
} finally {
|
||||
formModalProps.confirmLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record: Recordable) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: () => onEdit(record),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
||||
@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" title="自定义按钮" :width="1200" defaultFullscreen @cancel="onCancel">
|
||||
<template #footer>
|
||||
<a-button @click="onCancel">关闭</a-button>
|
||||
<div v-if="aiTestMode" style="float: left">
|
||||
<a-button @click="onGenButtons">生成测试数据</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #tableTitle>
|
||||
<a-button @click="onAdd" type="primary" preIcon="ant-design:plus">新增</a-button>
|
||||
<a-button @click="onOpenBIButton" preIcon="ant-design:setting">管理内置按钮</a-button>
|
||||
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="onBatchDelete">
|
||||
<a-icon type="delete" />
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button style="margin-left: 8px">
|
||||
批量操作
|
||||
<a-icon type="down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<BasicModal v-bind="formModalProps">
|
||||
<a-spin :spinning="formModalProps.confirmLoading">
|
||||
<BasicForm @register="registerForm" />
|
||||
</a-spin>
|
||||
</BasicModal>
|
||||
</BasicModal>
|
||||
|
||||
<!-- 管理内置按钮 -->
|
||||
<BuiltInButtonList @register="registerBIButtonModal" :record="record"/>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, reactive, computed, nextTick, defineComponent } from 'vue';
|
||||
|
||||
import { useOnlineTest } from '../../hooks/aitest/useOnlineTest';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
|
||||
import { ActionItem, BasicTable, TableAction } from '/@/components/Table';
|
||||
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form';
|
||||
import { list, doBatchDelete, saveOrUpdate } from './button.api';
|
||||
import { columns, formSchemas } from './button.data';
|
||||
import BuiltInButtonList from './BuiltInButtonList.vue';
|
||||
import {useMessage} from "@/hooks/web/useMessage";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CustomButtonList',
|
||||
components: { BasicModal, BasicTable, TableAction, BasicForm, BuiltInButtonList },
|
||||
emits: ['register'],
|
||||
setup() {
|
||||
const {createMessage: $message} = useMessage();
|
||||
|
||||
const code = ref('');
|
||||
const record = ref()
|
||||
// 列表页面公共参数、方法
|
||||
const { doRequest, doDeleteRecord, tableContext } = useListPage({
|
||||
tableProps: {
|
||||
api: (params) => list(code.value, params),
|
||||
columns,
|
||||
canResize: false,
|
||||
useSearchForm: false,
|
||||
beforeFetch(params) {
|
||||
return Object.assign(params, { column: 'orderNum', order: 'asc' });
|
||||
},
|
||||
},
|
||||
});
|
||||
// 注册table数据
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
// 注册弹窗
|
||||
const [registerModal, { closeModal }] = useModalInner(async (data) => {
|
||||
code.value = data.row.id;
|
||||
record.value = data.row
|
||||
reload();
|
||||
});
|
||||
// useOnlineAiTest
|
||||
const { aiTestMode, genButtons } = useOnlineTest({}, { reload }, null);
|
||||
// 注册 form 弹窗
|
||||
const [registerFormModal, formModal] = useModal();
|
||||
const isUpdate = ref(false);
|
||||
const formModalProps = reactive({
|
||||
onRegister: registerFormModal,
|
||||
title: computed(() => (isUpdate?.value ? '修改' : '新增')),
|
||||
width: 800,
|
||||
confirmLoading: false,
|
||||
onOk: onSubmit,
|
||||
onCancel: formModal.closeModal,
|
||||
});
|
||||
let formRecord = {};
|
||||
// 注册 form
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
// update-begin--author:liaozhiyang---date:20240618---for:【TV360X-1306】自定义按钮弹窗按钮样式切换是重置弹窗高度
|
||||
schemas: formSchemas({ redoModalHeight: formModal.redoModalHeight }),
|
||||
// update-end--author:liaozhiyang---date:20240618---for:【TV360X-1306】自定义按钮弹窗按钮样式切换是重置弹窗高度
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
async function openFormModal(data) {
|
||||
isUpdate.value = data.isUpdate;
|
||||
formRecord = { ...(data.record ?? {}) };
|
||||
formModal.openModal();
|
||||
await nextTick();
|
||||
await resetFields();
|
||||
setFieldsValue(formRecord);
|
||||
}
|
||||
|
||||
// 新增按钮
|
||||
function onAdd() {
|
||||
openFormModal({ isUpdate: false });
|
||||
}
|
||||
|
||||
// 编辑按钮
|
||||
function onEdit(record) {
|
||||
openFormModal({ isUpdate: true, record });
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function onGenButtons() {
|
||||
genButtons(code.value);
|
||||
}
|
||||
|
||||
// 批量删除事件
|
||||
async function onBatchDelete() {
|
||||
doRequest(() => doBatchDelete(selectedRowKeys.value));
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
formModalProps.confirmLoading = true;
|
||||
let values = await validate();
|
||||
values = Object.assign({ cgformHeadId: code.value }, formRecord, values);
|
||||
await saveOrUpdate(values, isUpdate.value);
|
||||
reload();
|
||||
formModal.closeModal();
|
||||
} finally {
|
||||
formModalProps.confirmLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- [Begin] 内置按钮弹窗 ------------------
|
||||
|
||||
const [registerBIButtonModal, bIButtonModal] = useModal()
|
||||
|
||||
/**
|
||||
* 打开管理内置按钮弹窗
|
||||
*/
|
||||
function onOpenBIButton() {
|
||||
if (record.value?.tableType == 3) {
|
||||
$message.warn('附表不支持管理内置按钮,请选择对应主表')
|
||||
return
|
||||
}
|
||||
bIButtonModal.openModal(true, {});
|
||||
}
|
||||
|
||||
// ---------------------- [End] 内置按钮弹窗 ------------------
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: () => onEdit(record),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
placement: 'left',
|
||||
confirm: () => doDeleteRecord(() => doBatchDelete([record.id])),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return {
|
||||
code,
|
||||
record,
|
||||
onAdd,
|
||||
onEdit,
|
||||
onBatchDelete,
|
||||
aiTestMode,
|
||||
onGenButtons,
|
||||
registerModal,
|
||||
registerTable,
|
||||
selectedRowKeys,
|
||||
rowSelection,
|
||||
onCancel,
|
||||
getTableAction,
|
||||
getDropDownAction,
|
||||
registerForm,
|
||||
formModalProps,
|
||||
registerBIButtonModal,
|
||||
onOpenBIButton,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
||||
@ -0,0 +1,42 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
export enum Api {
|
||||
list = '/online/cgform/button/list/',
|
||||
delete = '/online/cgform/button/delete',
|
||||
deleteBatch = '/online/cgform/button/deleteBatch',
|
||||
save = '/online/cgform/button/add',
|
||||
edit = '/online/cgform/button/edit',
|
||||
|
||||
builtInList = '/online/cgform/button/builtInList/',
|
||||
}
|
||||
|
||||
export const list = (code: string, params) => defHttp.get({ url: Api.list + code, params });
|
||||
|
||||
// 执行删除操作
|
||||
export function doBatchDelete(idList: string[]) {
|
||||
return defHttp.delete(
|
||||
{
|
||||
url: Api.deleteBatch,
|
||||
params: {
|
||||
ids: idList.join(','),
|
||||
},
|
||||
},
|
||||
{ joinParamsToUrl: true }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存或者更新
|
||||
*/
|
||||
export const saveOrUpdate = (params, isUpdate: boolean) => {
|
||||
if (isUpdate) {
|
||||
return defHttp.put({ url: Api.edit, params });
|
||||
} else {
|
||||
return defHttp.post({ url: Api.save, params });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 加载内置按钮列表
|
||||
*/
|
||||
export const builtInList = (code: string, params) => defHttp.get({url: Api.builtInList + code, params});
|
||||
@ -0,0 +1,194 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
// @ts-ignore
|
||||
import {getButtonIconRender} from "./button.data.tsx";
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{ title: '按钮编码', align: 'center', dataIndex: 'buttonCode' },
|
||||
{ title: '按钮名称', align: 'center', dataIndex: 'buttonName' },
|
||||
{
|
||||
title: '按钮样式',
|
||||
align: 'center',
|
||||
dataIndex: 'buttonStyle',
|
||||
customRender({ text, record }) {
|
||||
if (text === 'form') {
|
||||
let p = record.optPosition;
|
||||
return text + '(' + (p == '2' ? '底部' : '侧面') + ')';
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
},
|
||||
},
|
||||
{ title: '按钮类型', align: 'center', dataIndex: 'optType' },
|
||||
{ title: '排序', align: 'center', dataIndex: 'orderNum' },
|
||||
{
|
||||
title: '按钮图标',
|
||||
align: 'center',
|
||||
dataIndex: 'buttonIcon',
|
||||
customRender: ({text}) => {
|
||||
return getButtonIconRender({text});
|
||||
},
|
||||
},
|
||||
{ title: '表达式', align: 'center', dataIndex: 'exp' },
|
||||
{
|
||||
title: '按钮状态',
|
||||
align: 'center',
|
||||
dataIndex: 'buttonStatus',
|
||||
customRender({ text }) {
|
||||
if (text == 1) {
|
||||
return '激活';
|
||||
} else {
|
||||
return '未激活';
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchemas = ({ redoModalHeight }): FormSchema[] => {
|
||||
return [
|
||||
{
|
||||
label: '按钮编码',
|
||||
field: 'buttonCode',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
// update-begin--author:liaozhiyang---date:20240521---for:【TV360X-139】按钮编码加上正则校验
|
||||
dynamicRules: () => {
|
||||
return [
|
||||
{
|
||||
validator: (_, value) => {
|
||||
//需要return 一个Promise对象
|
||||
return new Promise((resolve, reject) => {
|
||||
const reg = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
||||
if (reg.test(value)) {
|
||||
resolve();
|
||||
} else {
|
||||
reject('编码只能包含字母、数字、下划线 (_) 和美元符号 ($)且不能以数字开头');
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
// update-begin--author:liaozhiyang---date:20240701---for:【TV360X-1693】自定义按钮编码排除sql和java系统内置编码
|
||||
{
|
||||
validator: (_, value) => {
|
||||
//需要return 一个Promise对象
|
||||
return new Promise((resolve, reject) => {
|
||||
const exclude = ['add', 'edit', 'detail', 'delete', 'batch_delete', 'import', 'export', 'query', 'reset', 'bpm', 'super_query', 'form_confirm'];
|
||||
if (exclude.includes(value)) {
|
||||
reject('不可使用内置按钮编码,请在“管理内置按钮”中修改内置按钮');
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
// update-end--author:liaozhiyang---date:20240701---for:【TV360X-1693】自定义按钮编码排除sql和java系统内置编码
|
||||
];
|
||||
},
|
||||
// update-end--author:liaozhiyang---date:20240521---for:【TV360X-139】按钮编码加上正则校验
|
||||
},
|
||||
{
|
||||
label: '按钮名称',
|
||||
field: 'buttonName',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: '按钮样式',
|
||||
field: 'buttonStyle',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: 'Link', value: 'link' },
|
||||
{ label: 'Button', value: 'button' },
|
||||
{ label: 'Form', value: 'form' },
|
||||
],
|
||||
// update-begin--author:liaozhiyang---date:20240618---for:【TV360X-1306】自定义按钮弹窗按钮样式切换是重置弹窗高度
|
||||
onChange: () => {
|
||||
redoModalHeight();
|
||||
},
|
||||
// update-end--author:liaozhiyang---date:20240618---for:【TV360X-1306】自定义按钮弹窗按钮样式切换是重置弹窗高度
|
||||
},
|
||||
defaultValue: 'link',
|
||||
},
|
||||
{
|
||||
label: '按钮位置',
|
||||
field: 'optPosition',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: false,
|
||||
options: [
|
||||
// { label: '侧面', value: '1' },
|
||||
{ label: '底部', value: '2' },
|
||||
],
|
||||
},
|
||||
defaultValue: '2',
|
||||
show: ({ model }) => model.buttonStyle === 'form',
|
||||
},
|
||||
{
|
||||
label: '按钮类型',
|
||||
field: 'optType',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: false,
|
||||
options: [
|
||||
{ label: 'Js', value: 'js' },
|
||||
{ label: 'Action', value: 'action' },
|
||||
],
|
||||
},
|
||||
defaultValue: 'js',
|
||||
},
|
||||
{
|
||||
label: '排序',
|
||||
field: 'orderNum',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
style: 'width: 100%',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '按钮图标',
|
||||
field: 'buttonIcon',
|
||||
// update-begin--author:liaozhiyang---date:20240528---for:【TV360X-136】按钮图标改成图标组件选择
|
||||
component: 'IconPicker',
|
||||
componentProps: {
|
||||
clearSelect: true,
|
||||
iconPrefixSave: false,
|
||||
},
|
||||
// update-end--author:liaozhiyang---date:20240528---for:【TV360X-136】按钮图标改成图标组件选择
|
||||
ifShow: ({ values, model }) => {
|
||||
if (values.buttonStyle == 'button' || values.buttonStyle == 'form') {
|
||||
return true;
|
||||
} else {
|
||||
// model.buttonIcon = null;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '表达式',
|
||||
field: 'exp',
|
||||
component: 'Input',
|
||||
// update-begin--author:liaozhiyang---date:20240603---for:【TV360X-89】自定义按钮样式是link时,展示表达式配置
|
||||
ifShow: ({ values, model }) => {
|
||||
if (values.buttonStyle == 'link') {
|
||||
return true;
|
||||
} else {
|
||||
model.exp = '';
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// update-end--author:liaozhiyang---date:20240603---for:【TV360X-89】自定义按钮样式是link时,展示表达式配置
|
||||
},
|
||||
{
|
||||
label: '按钮状态',
|
||||
field: 'buttonStatus',
|
||||
component: 'RadioButtonGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '激活', value: '1' },
|
||||
{ label: '未激活', value: '0' },
|
||||
],
|
||||
},
|
||||
defaultValue: '1',
|
||||
},
|
||||
]
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user