前端和后端源码,合并到一个git仓库中,方便用户下载,避免前后端不匹配的问题

This commit is contained in:
JEECG
2024-06-23 10:39:52 +08:00
parent bb918b742e
commit 0325e34dcb
1439 changed files with 171106 additions and 0 deletions

View File

@ -0,0 +1,57 @@
<template>
<PageWrapper>
<a-card :bordered="false">
<BasicTable :loading="loading" :dataSource="dataSource" @register="registerTable" @expand="onExpand" />
</a-card>
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { PageWrapper } from '/@/components/Page';
import { BasicTable, useTable } from '/@/components/Table';
const url = '/mock/api/asynTreeList';
const loading = ref<boolean>(false);
const dataSource = ref<any[]>([]);
const [registerTable, { setLoading }] = useTable({
rowKey: 'id',
bordered: true,
canResize: false,
// 树表格
isTreeTable: true,
showIndexColumn: true,
columns: [
{ title: '名称', dataIndex: 'name' },
{ title: '组件', dataIndex: 'component' },
{ title: '排序', dataIndex: 'orderNum' },
],
});
async function loadData(params) {
loading.value = true;
let result = await defHttp.get({ url, params });
loading.value = false;
return result.map((item) => {
if (item.hasChildren) {
return { ...item, children: [] };
} else {
return item;
}
});
}
async function loadRootData() {
dataSource.value = await loadData({ id: '0' });
}
loadRootData();
async function onExpand(isExpand, rowData) {
if (isExpand && rowData.hasChildren && rowData.children.length === 0) {
rowData.children = await loadData({ id: rowData.id });
}
}
</script>

View File

@ -0,0 +1,80 @@
<template>
<div>
<draggable @end="end" v-model="dataArr" item-key="id" style="display: flex">
<template #item="{ element }">
<div class="imgDiv">
<img :src="element.filePath" preview="index" />
</div>
</template>
<template #footer>
<a-button @click="sureChange" type="primary" style="margin-top: 100px">确定</a-button>
</template>
</draggable>
<a-divider>json数据</a-divider>
<a-row>
<a-col :span="12">
<p>拖拽前:</p>
<textarea rows="25" style="width: 780px">{{ oldDateSource }}</textarea>
</a-col>
<a-col :span="12">
<p>拖拽后:</p>
<textarea rows="25" style="width: 780px">{{ newDateSource }}</textarea>
</a-col>
</a-row>
</div>
</template>
<script lang="ts" setup>
import draggable from 'vuedraggable';
import { defineComponent, ref, unref } from 'vue';
const mockData = [
{ id: '000', sort: 0, filePath: 'https://static.jeecg.com/upload/test/1_1588149743473.jpg' },
{ id: '111', sort: 1, filePath: 'https://static.jeecg.com/upload/test/u27356337152749454924fm27gp0_1588149731821.jpg' },
{ id: '222', sort: 2, filePath: 'https://static.jeecg.com/upload/test/u24454681402491956848fm27gp0_1588149712663.jpg' },
{ id: '333', sort: 3, filePath: 'https://static.jeecg.com/temp/国炬软件logo_1606575029126.png' },
{ id: '444', sort: 4, filePath: 'https://static.jeecg.com/upload/test/u8891206113801177793fm27gp0_1588149704459.jpg' },
];
//数据集
const dataArr = ref(mockData);
//原始数据
const oldDateSource = ref(mockData);
//更改后的数据
const newDateSource = ref([]);
/**
* 拖动结束事件
* @param evt
*/
function end(evt) {
console.log('拖动前的位置' + evt.oldIndex);
console.log('拖动后的位置' + evt.newIndex);
}
/**
* 确认更改事件
* @param evt
*/
function sureChange() {
for (let i = 0; i < unref(dataArr).length; i++) {
dataArr.value[i].sort = i;
}
newDateSource.value = unref(dataArr);
}
</script>
<style scoped lang="less">
.imgDiv {
padding: 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
margin: 0 8px 8px 0;
img {
width: 120px;
height: 120px;
object-fit: cover;
}
}
</style>

View File

@ -0,0 +1,170 @@
<template>
<div style="min-width: 800px">
<a-row>
<!-- 左侧文件树 -->
<a-col :span="6">
<a-tree
showLine
:treeData="treeData"
:expandedKeys="[expandedKeys[0]]"
:selectedKeys="selectedKeys"
:style="{ height: '500px', 'border-right': '2px solid #c1c1c1', 'overflow-y': 'auto' }"
@expand="onExpand"
@select="onSelect"
></a-tree>
</a-col>
<!--右侧缩略图-->
<a-col :span="18">
<a-row style="margin-top: 10px; padding-left: 2%">
<a-col :span="24" style="margin-bottom: 10px">
<a-button @click="prev" preIcon="ant-design:left-outlined" type="primary">上一页</a-button>
<a-button @click="next" preIcon="ant-design:right-outlined" style="margin-left: 8px" type="primary">下一页</a-button>
<span style="margin-left: 100px; font-weight: bolder">{{ navName }}</span>
</a-col>
<a-col :span="24">
<img :src="imgUrl" preview />
</a-col>
</a-row>
</a-col>
</a-row>
</div>
</template>
<script lang="ts" setup>
import { ref, unref, onMounted } from 'vue';
//mock数据
const mockdata = [
{
title: '第一页',
key: '0-0',
children: [
{
title: '1页',
key: '0-0-0',
imgUrl: 'https://static.jeecg.com/upload/test/1_1588149743473.jpg',
},
{
title: '2页',
key: '0-0-1',
imgUrl: 'https://static.jeecg.com/upload/test/u27356337152749454924fm27gp0_1588149731821.jpg',
},
],
},
{
title: '第二页',
key: '0-1',
children: [
{
title: '1页',
key: '0-1-0',
imgUrl: 'https://static.jeecg.com/upload/test/u24454681402491956848fm27gp0_1588149712663.jpg',
},
{
title: '2页',
key: '0-1-1',
imgUrl: 'https://static.jeecg.com/upload/test/u8891206113801177793fm27gp0_1588149704459.jpg',
},
],
},
{
title: '第三页',
key: '0-2',
children: [
{
title: '1页',
key: '0-2-0',
imgUrl: 'https://static.jeecg.com/upload/test/1374962_1587621329085.jpg',
},
],
},
];
/**
* 左侧树形数据
*/
const treeData = ref(mockdata);
//选中的key
const selectedKeys = ref([]);
//展开的key
const expandedKeys = ref([]);
const sort = ref(0);
//图片链接
const imgUrl = ref('');
//页码标题
const navName = ref('');
//图片集合
const imgList = ref([]);
onMounted(getImgList);
/**
* 加载图片集合
*/
function getImgList() {
var count = 0;
for (var i = 0; i < unref(treeData).length; i++) {
for (var j = 0; j < unref(treeData)[i].children.length; j++) {
imgList.value.push({
key: unref(treeData)[i].children[j].key,
pkey: unref(treeData)[i].key,
sort: count++,
imgUrl: unref(treeData)[i].children[j].imgUrl,
navName: unref(treeData)[i].title + '/' + unref(treeData)[i].children[j].title,
});
}
}
setValue(imgList.value[unref(sort)]);
}
/**
* 节点选中事件
*/
function onSelect(selectedKeys, info) {
for (var i = 0; i < unref(imgList).length; i++) {
if (unref(imgList)[i].key === selectedKeys[0]) {
sort.value = unref(imgList)[i].sort;
setValue(unref(imgList)[i]);
break;
}
}
}
/**
* 节点展开事件
*/
function onExpand(expandedKey) {
expandedKeys.value = [];
if (expandedKey !== null && expandedKey !== '') {
expandedKeys.value[0] = expandedKey[1];
}
}
/**
* 上一页
*/
function prev() {
if (unref(sort) === 0) {
sort.value = unref(imgList).length - 1;
} else {
sort.value = sort.value - 1;
}
setValue(unref(imgList)[unref(sort)]);
}
/**
* 下一页
*/
function next() {
if (unref(sort) === unref(imgList).length - 1) {
sort.value = 0;
} else {
sort.value = unref(sort) + 1;
}
setValue(unref(imgList)[unref(sort)]);
}
// 设置受控节点值
function setValue(value) {
selectedKeys.value = [];
imgUrl.value = value.imgUrl;
selectedKeys.value[0] = value.key;
expandedKeys.value[0] = value.pkey;
navName.value = value.navName;
}
</script>

View File

@ -0,0 +1,243 @@
<template>
<a-card :bordered="false">
<BasicTable @register="registerTable" :expandedRowKeys="expandedRowKeys" :rowSelection="rowSelection" @expand="handleExpand">
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
</template>
<template #expandedRowRender>
<BasicTable bordered size="middle" rowKey="id" :canResize="false" :columns="innerColumns" :dataSource="innerData" :pagination="false">
</BasicTable>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<JVxeTableModal @register="registerModal" @success="reload()"></JVxeTableModal>
</a-card>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import JVxeTableModal from '/@/views/demo/vextable/jvxetable/JVxeTableModal.vue';
//接口url
const url = {
list: '/test/order/orderList',
delete: '/test/order/delete',
deleteBatch: '/test/order/deleteBatch',
customerListByMainId: '/test/order/listOrderCustomerByMainId',
};
// 展开key
const expandedRowKeys = ref<any[]>([]);
// 选择key
const checkedKeys = ref<any[]>([]);
// 子表数据
const innerData = ref<any[]>([]);
// 主表表头
const columns = [
{
title: '订单号',
align: 'center',
dataIndex: 'orderCode',
width: 100,
},
{
title: '订单类型',
align: 'center',
dataIndex: 'ctype',
width: 100,
customRender: ({ text }) => {
let re = '';
if (text === '1') {
re = '国内订单';
} else if (text === '2') {
re = '国际订单';
}
return re;
},
},
{
title: '订单日期',
align: 'center',
width: 100,
dataIndex: 'orderDate',
},
{
title: '订单金额',
align: 'center',
dataIndex: 'orderMoney',
width: 100,
},
{
title: '订单备注',
align: 'center',
dataIndex: 'content',
width: 100,
},
];
// 子表表头
const innerColumns = [
{
title: '客户名',
align: 'center',
width: 100,
dataIndex: 'name',
key: 'name',
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
customRender: function (text) {
//console.log(typeof text )
//console.log(text)
if (text.value == '1') {
return '男';
} else if (text.value == '2') {
return '女';
} else {
return text;
}
},
},
{
title: '身份证号码',
align: 'center',
dataIndex: 'idcard',
},
{
title: '电话',
dataIndex: 'telphone',
align: 'center',
},
];
const list = (params) => defHttp.get({ url: url.list, params });
const [registerModal, { openModal }] = useModal();
const [registerTable, { reload }] = useTable({
columns,
api: list,
rowKey: 'id',
striped: true,
useSearchForm: false,
showTableSetting: true,
clickToRowSelect: false,
bordered: true,
actionColumn: {
width: 110,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: undefined,
},
});
/**
* 选择列配置
*/
const rowSelection = {
type: 'checkbox',
columnWidth: 30,
selectedRowKeys: checkedKeys,
onChange: onSelectChange,
};
/**
* 选择事件
*/
function onSelectChange(selectedRowKeys: (string | number)[]) {
checkedKeys.value = selectedRowKeys;
}
/**
* 展开事件
* */
function handleExpand(expanded, record) {
expandedRowKeys.value = [];
innerData.value = [];
if (expanded === true) {
expandedRowKeys.value.push(record.id);
defHttp.get({ url: url.customerListByMainId, params: { orderId: record.id } }, { isTransformResponse: false }).then((res) => {
if (res.success) {
innerData.value = res.result.records;
}
});
}
}
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑事件
*/
function handleEdit(record) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 删除事件
*/
function handleDelete(record) {
defHttp.delete({ url: url.delete, data: { id: record.id } }, { joinParamsToUrl: true }).then(() => {
reload();
});
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
];
}
</script>
<style scoped>
.ant-card-body .table-operator {
margin-bottom: 18px;
}
.ant-table-tbody .ant-table-row td {
padding-top: 15px;
padding-bottom: 15px;
}
.anty-row-operator button {
margin: 0 5px;
}
.ant-btn-danger {
background-color: #ffffff;
}
.ant-modal-cust-warp {
height: 100%;
}
.ant-modal-cust-warp .ant-modal-body {
height: calc(100% - 110px) !important;
overflow-y: auto;
}
.ant-modal-cust-warp .ant-modal-content {
height: 90% !important;
overflow-y: hidden;
}
</style>

View File

@ -0,0 +1,67 @@
<template>
<BasicForm
:labelWidth="200"
:schemas="schemas"
:showResetButton="false"
:showSubmitButton="false"
:actionColOptions="{ span: 24 }"
@submit="handleSubmit"
@reset="handleReset"
style="height: 800px"
>
<template #jCodeEdit="{ model, field }">
<JCodeEditor v-model:value="model[field]" mode="js" height="300px" :fullScreen="true" />
</template>
</BasicForm>
</template>
<script lang="ts">
import { computed, defineComponent, unref, ref } from 'vue';
import { BasicForm, FormSchema, JCodeEditor } from '/@/components/Form';
import { useMessage } from '/@/hooks/web/useMessage';
import { optionsListApi } from '/@/api/demo/select';
import { useDebounceFn } from '@vueuse/core';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'JCodeEditor',
label: '代码编辑器',
required: true,
slot: 'jCodeEdit',
colProps: {
span: 15,
},
defaultValue: 'Hello JeecgBoot',
},
];
export default defineComponent({
components: { BasicForm, JCodeEditor },
setup() {
const check = ref(null);
const { createMessage } = useMessage();
const keyword = ref<string>('');
const searchParams = computed<Recordable>(() => {
return { keyword: unref(keyword) };
});
function onSearch(value: string) {
keyword.value = value;
}
return {
schemas,
optionsListApi,
onSearch: useDebounceFn(onSearch, 300),
searchParams,
handleReset: () => {
keyword.value = '';
},
handleSubmit: (values: any) => {
createMessage.success('click search,values:' + JSON.stringify(values));
},
check,
};
},
});
</script>

View File

@ -0,0 +1,99 @@
<template>
<div class="p-4">
<div class="p-4 bg-white">
<a-button-group class="j-table-operator">
<a-button type="primary" @click="setDis(0)">启用</a-button>
<a-button type="primary" @click="setDis(1)">禁用</a-button>
<a-button type="primary" @click="getValues()">校验表单并获取值</a-button>
<a-button type="primary" @click="setValues()">设置值</a-button>
</a-button-group>
<BasicForm @register="register" @submit="handleSubmit" />
</div>
</div>
</template>
<script lang="ts">
export default {
title: '富文本 | Markdown',
name: 'MarkdownDemo',
};
</script>
<script lang="ts" setup>
import { FormSchema, useForm, BasicForm } from '/@/components/Form';
import { useMessage } from '/@/hooks/web/useMessage';
const { createMessage, createSuccessModal } = useMessage();
const schemas: FormSchema[] = [
{
field: 'name',
component: 'Input',
label: '姓名',
required: true,
defaultValue: 'zhangsan',
},
{
field: 'tinymce',
component: 'JEditor',
label: '富文本',
defaultValue: 'defaultValue',
required: true,
},
{
field: 'markdown',
component: 'JMarkdownEditor',
label: 'Markdown',
defaultValue: '# 张三',
required: true,
componentProps: {
height: 300,
},
},
];
const [register, { setProps, validate, setFieldsValue }] = useForm({
labelWidth: 120,
schemas: schemas,
actionColOptions: {
span: 24,
},
compact: true,
showResetButton: false,
showSubmitButton: false,
showAdvancedButton: false,
disabled: false,
});
function handleSubmit(values) {
console.log(values);
}
function setDis(flag) {
setProps({ disabled: !!flag });
}
async function getValues() {
try {
const values = await validate();
console.log(values);
createSuccessModal({
title: '校验通过',
content: `${JSON.stringify(values)}`,
});
} catch (error) {
createMessage.warning('检验不通过');
}
}
function setValues() {
setFieldsValue({
name: 'LiSi',
markdown: '# 李四',
tinymce: '<p><strong><span style="font-size: 18pt;">张<span style="color: #e03e2d;">三</span>丰</span></strong></p>',
});
}
</script>
<style scoped></style>

View File

@ -0,0 +1,84 @@
<template>
<a-button-group class="j-table-operator">
<a-button type="primary" @click="setDisabled(0)">启用</a-button>
<a-button type="primary" @click="setDisabled(1)">禁用</a-button>
<a-button type="primary" @click="showUploadModal">文件弹窗</a-button>
</a-button-group>
<BasicForm @register="register" @submit="handleSubmit" />
<JUploadModal v-model:value="uploadModalValue" @register="registerModel" />
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { FormSchema, useForm, BasicForm } from '/@/components/Form';
import { UploadTypeEnum } from '/@/components/Form/src/jeecg/components/JUpload';
import { JUploadModal } from '/@/components/Form/src/jeecg/components/JUpload';
import { useModal } from '/@/components/Modal';
const uploadModalValue = ref('');
const schemas: FormSchema[] = [
{
field: 'uploadFile',
component: 'JUpload',
helpMessage: '无限制上传',
label: '上传文件',
},
{
field: 'uploadFileMax',
component: 'JUpload',
helpMessage: '最多上传3个文件',
label: '上传文件3',
componentProps: { maxCount: 3 },
},
{
field: 'uploadImage',
component: 'JUpload',
label: '上传图片',
helpMessage: '无限制上传',
componentProps: {
fileType: UploadTypeEnum.image,
},
},
{
field: 'uploadImageMax',
component: 'JUpload',
label: '上传图片1',
helpMessage: '最多上传1张图片',
componentProps: {
fileType: UploadTypeEnum.image,
maxCount: 1,
},
},
];
const [registerModel, { openModal }] = useModal();
const [register, { setProps, validate, setFieldsValue }] = useForm({
labelWidth: 120,
schemas: schemas,
actionColOptions: {
span: 24,
},
compact: true,
showResetButton: false,
showSubmitButton: false,
showAdvancedButton: false,
disabled: false,
});
function handleSubmit(values) {
console.log(values);
}
function setDisabled(flag) {
setProps({ disabled: !!flag });
}
function showUploadModal() {
openModal(true, {
maxCount: 9,
fileType: UploadTypeEnum.image,
});
}
</script>

View File

@ -0,0 +1,403 @@
<template>
<a-space>
<a-button @click="onToggleLoading">切换加载</a-button>
<a-button @click="onToggleDisabled">切换禁用</a-button>
</a-space>
<!--这种使用场景得用height用maxHeight底层有问题-->
<JVxeTable
ref="tableRef"
stripe
toolbar
rowNumber
rowSelection
rowExpand
resizable
asyncRemove
clickSelectRow
:height="480"
:checkboxConfig="{ range: true }"
:disabledRows="{ input: ['text--16', 'text--18'] }"
:loading="loading"
:disabled="disabled"
:columns="columns"
:dataSource="dataSource"
@removed="onJVxeRemove"
@valueChange="handleValueChange"
@blur="handleBlur"
:custom="true"
>
<template #toolbarSuffix>
<a-button @click="handleTableCheck">表单验证</a-button>
<a-tooltip placement="top" title="获取值,忽略表单验证" :autoAdjustOverflow="true">
<a-button @click="onGetData">获取数据</a-button>
</a-tooltip>
<a-tooltip placement="top" title="模拟加载1000条数据" :autoAdjustOverflow="true">
<a-button @click="handleTableSet">设置值</a-button>
</a-tooltip>
<a-button @click="onGetSelData">获取选中数据</a-button>
<a-button @click="onClearSel">清空选中</a-button>
<a-button @click="onDelFirst">删除第一行数据</a-button>
<a-button @click="onDelSel">删除选中数据</a-button>
</template>
<template #expandContent="props">
<div style="padding: 20px">
<span>Hello! My name is: {{ props.row.input }}!</span>
</div>
</template>
<template #myAction="props">
<a @click="onLookRow(props)">查看</a>
<a-divider type="vertical" />
<Popconfirm title="确定删除吗?" @confirm="onDeleteRow(props)">
<a>删除</a>
</Popconfirm>
</template>
</JVxeTable>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
// noinspection ES6UnusedImports
import { Popconfirm } from 'ant-design-vue';
import { JVxeTypes, JVxeColumn, JVxeTableInstance } from '/@/components/jeecg/JVxeTable/types';
import { useMessage } from '/@/hooks/web/useMessage';
import { random } from 'lodash-es';
import { buildUUID } from '/@/utils/uuid';
import dayjs from 'dayjs';
import { pushIfNotExist } from '/@/utils/common/compUtils';
const { createMessage } = useMessage();
const tableRef = ref<JVxeTableInstance>();
const loading = ref(false);
const disabled = ref(false);
const columns = ref<JVxeColumn[]>([
{
title: 'ID',
key: 'id',
type: JVxeTypes.hidden,
},
{
title: '不可编辑',
key: 'noEdit',
type: JVxeTypes.normal,
width: 180,
defaultValue: 'noEdit-new',
},
{
title: '单行文本',
key: 'input',
type: JVxeTypes.input,
width: 180,
defaultValue: '',
placeholder: '请输入${title}',
validateRules: [
{
required: true, // 必填
message: '请输入${title}', // 显示的文本
},
{
pattern: /^[a-z|A-Z][a-z|A-Z\d_-]*$/, // 正则
message: '必须以字母开头可包含数字下划线横杠',
},
{
unique: true,
message: '${title}不能重复',
},
{
handler({ cellValue, row, column }, callback, target) {
// cellValue 当前校验的值
// callback(flag, message) 方法必须执行且只能执行一次
// flag = 是否通过了校验,不填写或者填写 null 代表不进行任何操作
// message = 提示的类型,默认使用配置的 message
// target 行编辑的实例对象
if (cellValue === 'abc') {
callback(false, '${title}不能是abc'); // false = 未通过校验
} else {
callback(true); // true = 通过验证
}
},
message: '${title}默认提示',
},
],
},
{
title: '多行文本',
key: 'textarea',
type: JVxeTypes.textarea,
width: 200,
},
{
title: '数字',
key: 'number',
type: JVxeTypes.inputNumber,
width: 80,
defaultValue: 32,
// 【统计列】sum = 求和、average = 平均值
statistics: ['sum', 'average'],
},
{
title: '下拉框',
key: 'select',
type: JVxeTypes.select,
width: 180,
// 下拉选项
options: [
{ title: 'String', value: 'string' },
{ title: 'Integer', value: 'int' },
{ title: 'Double', value: 'double' },
{ title: 'Boolean', value: 'boolean' },
],
// allowInput: true,
allowSearch: true,
placeholder: '请选择',
},
{
title: '下拉框_字典',
key: 'select_dict',
type: JVxeTypes.select,
width: 180,
options: [],
dictCode: 'sex',
placeholder: '请选择',
},
{
title: '下拉框_多选',
key: 'select_multiple',
type: JVxeTypes.selectMultiple,
width: 205,
options: [
{ title: 'String', value: 'string' },
{ title: 'Integer', value: 'int' },
{ title: 'Double', value: 'double' },
{ title: 'Boolean', value: 'boolean' },
],
defaultValue: ['int', 'boolean'], // 多个默认项
// defaultValue: 'string,double,int', // 也可使用这种方式
placeholder: '多选',
},
{
title: '下拉框_搜索',
key: 'select_search',
type: JVxeTypes.selectSearch,
width: 180,
options: [
{ title: 'String', value: 'string' },
{ title: 'Integer', value: 'int' },
{ title: 'Double', value: 'double' },
{ title: 'Boolean', value: 'boolean' },
],
},
{
title: '日期时间',
key: 'datetime',
type: JVxeTypes.datetime,
width: 200,
defaultValue: '2019-04-30 14:52:22',
placeholder: '请选择',
},
{
title: '时间',
key: 'time',
type: JVxeTypes.time,
width: 200,
defaultValue: '14:52:22',
placeholder: '请选择',
},
{
title: '复选框',
key: 'checkbox',
type: JVxeTypes.checkbox,
width: 100,
customValue: ['Y', 'N'], // true ,false
defaultChecked: false,
},
{
title: '操作',
key: 'action',
type: JVxeTypes.slot,
fixed: 'right',
minWidth: 120,
align: 'center',
slotName: 'myAction',
},
]);
const dataSource = ref<any[]>([]);
/* 随机生成数据 */
function randomPage(current, pageSize, isLoading = false) {
if (isLoading) {
loading.value = true;
}
let randomDatetime = () => {
let time = random(1000, 9999999999999);
return dayjs(new Date(time)).format('YYYY-MM-DD HH:mm:ss');
};
let limit = (current - 1) * pageSize;
let options = ['string', 'int', 'double', 'boolean'];
let begin = Date.now();
let values: any[] = [];
for (let i = 0; i < pageSize; i++) {
values.push({
id: buildUUID(),
noEdit: `noEdit-${limit + i + 1}`,
input: `text-${limit + i + 1}`,
textarea: `textarea-${limit + i + 1}`,
number: random(0, 233),
select: options[random(0, 3)],
select_dict: random(1, 2).toString(),
select_multiple: (() => {
let length = random(1, 4);
let arr = [];
for (let j = 0; j < length; j++) {
pushIfNotExist(arr, options[random(0, 3)]);
}
return arr.join(',');
})(),
select_search: options[random(0, 3)],
datetime: randomDatetime(),
checkbox: ['Y', 'N'][random(0, 1)],
});
}
dataSource.value = values;
let end = Date.now();
let diff = end - begin;
if (isLoading && diff < pageSize) {
setTimeout(() => (loading.value = false), pageSize - diff);
}
}
randomPage(0, 20, true);
function onLookRow(props) {
createMessage.success('请在控制台查看输出');
// 参数介绍:
// props.value 当前单元格的值
// props.row 当前行的数据
// props.rowId 当前行ID
// props.rowIndex 当前行下标
// props.column 当前列的配置
// props.columnIndex 当前列下标
// props.$table vxe实例可以调用vxe内置方法
// props.target JVXE实例可以调用JVXE内置方法
// props.caseId JVXE实例唯一ID
// props.scrolling 是否正在滚动
// props.triggerChange 触发change事件用于更改slot的值
console.log('查看: ', { props });
}
// async function onDeleteRow(props) {
// // 同步调用删除方法
// const res = await tableRef.value?.removeRows(props.row);
// if (res && res.rows.length > 0) {
// createMessage.success('删除成功');
// }
// }
async function onDeleteRow(props) {
// 异步调用删除方法
const res = await tableRef.value?.removeRows(props.row, true);
console.log('删除成功~', res);
}
function handleValueChange(event) {
console.log('handleValueChange.event: ', event);
}
// update-begin--author:liaozhiyang---date:20230817---for【issues/636】JVxeTable加上blur事件
function handleBlur(event){
console.log("blur",event);
}
// update-end--author:liaozhiyang---date:20230817---for【issues/636】JVxeTable加上blur事件
/** 表单验证 */
function handleTableCheck() {
tableRef.value!.validateTable().then((errMap) => {
if (errMap) {
console.log('表单验证未通过', { errMap });
createMessage.error('验证未通过请在控制台查看详细');
} else {
createMessage.success('验证通过');
}
});
}
/** 获取值,忽略表单验证 */
function onGetData() {
const values = tableRef.value!.getTableData();
console.log('获取值:', { values });
createMessage.success('获取值成功请看控制台输出');
}
/** 模拟加载1000条数据 */
function handleTableSet() {
randomPage(1, 1000, true);
}
function onDelFirst() {
const xTable = tableRef.value!.getXTable();
const record = xTable.getTableData().fullData[0];
tableRef.value!.removeRows(record);
}
function onDelSel() {
const xTable = tableRef.value!.getXTable();
xTable.removeCheckboxRow();
}
function onGetSelData() {
createMessage.info('请看控制台');
console.log(tableRef.value!.getSelectionData());
}
function onClearSel() {
tableRef.value!.clearSelection();
}
function onToggleLoading() {
loading.value = !loading.value;
}
function onToggleDisabled() {
disabled.value = !disabled.value;
}
function doDelete(deleteRows) {
let rowId;
return new Promise((resolve) => {
if (Array.isArray(deleteRows)) {
rowId = deleteRows.filter((row) => row.id);
} else {
rowId = deleteRows.id;
}
console.log('删除 rowId: ', rowId);
setTimeout(() => resolve(true), 1500);
});
}
/** 异步删除示例 */
async function onJVxeRemove(event) {
const hideLoading = createMessage.loading('删除中', 0);
try {
// 1. 向后台传递 event.deleteRows 以删除
let flag = await doDelete(event.deleteRows);
if (flag) {
// 注:如果启用了表格的 loading 状态,则必须先停止再删除,否则会导致无法从表格上删除数据
// 2. 调用 event.confirmRemove 方法确认删除成功
// await tableRef.value!.removeSelection();
await event.confirmRemove()
createMessage.success('删除成功');
} else {
// 3. 若删除失败,不调用 event.confirmRemove() 方法就不会删除数据
createMessage.warn('删除失败');
}
} finally {
hideLoading();
}
}
</script>

View File

@ -0,0 +1,179 @@
<template>
<JVxeTable
ref="tableRef"
toolbar
row-number
row-selection
keep-source
:height="492"
:loading="loading"
:dataSource="dataSource"
:columns="columns"
:pagination="pagination"
style="margin-top: 8px"
@pageChange="handlePageChange"
>
<template #toolbarSuffix>
<a-button @click="handleTableGet">获取数据</a-button>
</template>
</JVxeTable>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
// noinspection ES6UnusedImports
import { Popconfirm } from 'ant-design-vue';
import { JVxeColumn, JVxeTableInstance, JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
import { useMessage } from '/@/hooks/web/useMessage';
import { random } from 'lodash-es';
import { buildUUID } from '/@/utils/uuid';
import { uploadUrl } from '/@/api/common/api';
const tableRef = ref<JVxeTableInstance>();
const { createMessage } = useMessage();
const loading = ref(false);
const columns = ref<JVxeColumn[]>([
{
title: '下拉框_字典表搜索',
key: 'select_dict_search',
type: JVxeTypes.selectDictSearch,
width: 200,
async: true, // 异步搜索,默认为 true
// 字典表配置信息:数据库表名,显示字段名,存储字段名
dict: 'sys_user,realname,username',
tipsContent: '请输入查询条件',
},
{
title: 'JPopup',
key: 'popup',
type: JVxeTypes.popup,
width: 180,
popupCode: 'demo',
// field: 'name,sex,age',
// orgFields: 'name,sex,age',
// destFields: 'popup,popup_sex,popup_age',
fieldConfig: [
{ source: 'name', target: 'popup' },
{ source: 'sex', target: 'popup_sex' },
{ source: 'age', target: 'popup_age' },
],
},
{
title: 'JP-性别',
key: 'popup_sex',
type: JVxeTypes.select,
dictCode: 'sex',
disabled: true,
width: 100,
},
{
title: 'JP-年龄',
key: 'popup_age',
type: JVxeTypes.normal,
width: 80,
},
{
title: '用户选择',
key: 'userSelect',
type: JVxeTypes.userSelect,
width: 240,
},
{
title: '部门选择',
key: 'departSelect',
type: JVxeTypes.departSelect,
width: 240,
},
{
title: '单选',
key: 'radio',
type: JVxeTypes.radio,
width: 130,
options: [
{ text: '男', value: '1' },
{ text: '女', value: '2' },
],
// 允许清除选择(再点一次取消选择)
allowClear: false,
},
{
title: '上传',
key: 'upload',
type: JVxeTypes.upload,
width: 180,
btnText: '点击上传',
token: true,
responseName: 'message',
action: uploadUrl,
},
{
title: '图片上传',
key: 'image',
type: JVxeTypes.image,
width: 180,
maxCount: 6,
},
{
title: '文件上传',
key: 'file',
type: JVxeTypes.file,
width: 180,
maxCount: 2,
},
{
title: '进度条',
key: 'progress',
type: JVxeTypes.progress,
minWidth: 320,
},
]);
const dataSource = ref<any[]>([]);
const pagination = reactive({
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30', '100', '200'],
total: 1000,
});
randomPage(pagination.current, pagination.pageSize, true);
// 当分页参数变化时触发的事件
function handlePageChange(event) {
// 重新赋值
pagination.current = event.current;
pagination.pageSize = event.pageSize;
// 查询数据
randomPage(event.current, event.pageSize, true);
}
/** 获取值,忽略表单验证 */
function handleTableGet() {
const values = tableRef.value!.getTableData();
console.log('获取值:', { values });
createMessage.success('获取值成功请看控制台输出');
}
/* 随机生成分页数据 */
function randomPage(current, pageSize, $loading = false) {
if ($loading) {
loading.value = true;
}
let begin = Date.now();
let values: any[] = [];
for (let i = 0; i < pageSize; i++) {
let radio = random(0, 2);
values.push({
id: buildUUID(),
select_dict_search: ['admin', '', 'jeecg'][random(0, 2)],
progress: random(0, 100),
radio: radio ? radio.toString() : null,
});
}
dataSource.value = values;
let end = Date.now();
let diff = end - begin;
if ($loading && diff < pageSize) {
setTimeout(() => (loading.value = false), pageSize - diff);
}
}
</script>

View File

@ -0,0 +1,129 @@
<template>
<div>
<ol style="border: 1px solid #cccccc; width: 600px; padding: 8px">
<li>1. 开启 dragSort 属性之后即可实现上下拖拽排序</li>
<li>2. 使用 sortKey 属性可以自定义排序保存的 key默认为 orderNum</li>
<li>3. 使用 sortBegin 属性可以自定义排序的起始值默认为 0</li>
<li>4. sortKey 定义的字段不需要定义在 columns 中也能正常获取到值</li>
<li>5. 当存在 fixed 列时拖拽排序将会失效仅能上下排序</li>
</ol>
<p> 以下示例开启了拖拽排序排序值保存字段为 sortNum排序起始值为 3<br /> </p>
<JVxeTable
ref="tableRef1"
toolbar
dragSort
sortKey="sortNum"
:sortBegin="3"
rowSelection
dragSortFixed="none"
rowSelectionFixed="none"
:maxHeight="580"
:columns="table1.columns"
:dataSource="table1.data"
>
<template #toolbarSuffix>
<a-button @click="onGetData1">获取数据</a-button>
</template>
</JVxeTable>
<br />
<p>以下 fixed 表格不支持拖拽排序仅支持点击上下排序</p>
<JVxeTable ref="tableRef2" toolbar dragSort rowSelection :maxHeight="580" :columns="table2.columns" :dataSource="table2.data">
<template #toolbarSuffix>
<a-button @click="onGetData2">获取数据</a-button>
</template>
</JVxeTable>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { JVxeTypes, JVxeColumn, JVxeTableInstance } from '/@/components/jeecg/JVxeTable/types';
import { useMessage } from '/@/hooks/web/useMessage';
const tableRef1 = ref<JVxeTableInstance>();
const tableRef2 = ref<JVxeTableInstance>();
const table1 = reactive({
columns: [
{
title: 'ID',
key: 'id',
width: 120,
type: JVxeTypes.normal,
},
{
title: '姓名',
key: 'name',
width: 240,
type: JVxeTypes.input,
defaultValue: 'new name',
},
{
title: '字段长度',
key: 'dbLength',
width: 2400,
type: JVxeTypes.inputNumber,
defaultValue: 32,
},
{
title: 'sortNum',
key: 'sortNum',
width: 120,
type: JVxeTypes.normal,
},
] as JVxeColumn[],
data: [
{ id: 'uuid-0001', name: '张三', dbLength: 123 },
{ id: 'uuid-0002', name: '李四', dbLength: 777 },
{ id: 'uuid-0003', name: '王五', dbLength: 666 },
{ id: 'uuid-0004', name: '赵六', dbLength: 233 },
],
});
const table2 = reactive({
columns: [
{
title: 'ID',
key: 'id',
width: 320,
fixed: 'left',
type: JVxeTypes.normal,
},
{
title: '姓名',
key: 'name',
width: 720,
type: JVxeTypes.input,
defaultValue: 'new name',
},
{
title: '字段长度',
key: 'dbLength',
width: 720,
type: JVxeTypes.inputNumber,
defaultValue: 32,
},
] as JVxeColumn[],
data: [
{ id: 'uuid-0001', name: '张三', dbLength: 123 },
{ id: 'uuid-0002', name: '李四', dbLength: 777 },
{ id: 'uuid-0003', name: '王五', dbLength: 666 },
{ id: 'uuid-0004', name: '赵六', dbLength: 233 },
],
});
const { createMessage } = useMessage();
function onGetData1() {
createMessage.info('请看控制台');
console.log(tableRef1.value!.getTableData());
}
function onGetData2() {
createMessage.info('请看控制台');
console.log(tableRef2.value!.getTableData());
}
</script>

View File

@ -0,0 +1,153 @@
<template>
<JVxeTable
ref="vTable"
toolbar
rowNumber
rowSelection
:maxHeight="580"
:dataSource="dataSource"
:columns="columns"
:linkageConfig="linkageConfig"
/>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { JVxeTypes, JVxeColumn, JVxeLinkageConfig } from '/@/components/jeecg/JVxeTable/types';
// 联动配置
const linkageConfig = ref<JVxeLinkageConfig[]>([
{ requestData: requestMockData, key: 's1' },
// 可配置多个联动
{ requestData: requestMenu, key: 'menu1' },
]);
const columns = ref<JVxeColumn[]>([
{
title: '性别',
key: 'sex',
type: JVxeTypes.select,
dictCode: 'sex',
width: '180px',
placeholder: '请选择${title}',
},
{
title: '/直辖市/自治区',
key: 's1',
type: JVxeTypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 's2',
},
{
title: '市',
key: 's2',
type: JVxeTypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 's3',
},
{
title: '/',
key: 's3',
type: JVxeTypes.select,
width: '180px',
options: [],
placeholder: '请选择${title}',
},
{
title: '一级菜单',
key: 'menu1',
type: JVxeTypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 'menu2',
},
{
title: '二级菜单',
key: 'menu2',
type: JVxeTypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 'menu3',
},
{
title: '三级菜单',
key: 'menu3',
type: JVxeTypes.select,
width: '180px',
placeholder: '请选择${title}',
},
]);
const dataSource = ref([
{ sex: '1', s1: '110000', s2: '110100', s3: '110101' },
{ sex: '2', s1: '130000', s2: '130300', s3: '130303' },
]);
// 模拟数据
const mockData = [
{ text: '北京市', value: '110000', parent: '' },
{ text: '天津市', value: '120000', parent: '' },
{ text: '河北省', value: '130000', parent: '' },
{ text: '上海市', value: '310000', parent: '' },
{ text: '北京市', value: '110100', parent: '110000' },
{ text: '天津市市', value: '120100', parent: '120000' },
{ text: '石家庄市', value: '130100', parent: '130000' },
{ text: '唐山市', value: '130200', parent: '130000' },
{ text: '秦皇岛市', value: '130300', parent: '130000' },
{ text: '上海市', value: '310100', parent: '310000' },
{ text: '东城区', value: '110101', parent: '110100' },
{ text: '西城区', value: '110102', parent: '110100' },
{ text: '朝阳区', value: '110105', parent: '110100' },
{ text: '和平区', value: '120101', parent: '120100' },
{ text: '河东区', value: '120102', parent: '120100' },
{ text: '河西区', value: '120103', parent: '120100' },
{ text: '黄浦区', value: '310101', parent: '310100' },
{ text: '徐汇区', value: '310104', parent: '310100' },
{ text: '长宁区', value: '310105', parent: '310100' },
{ text: '长安区', value: '130102', parent: '130100' },
{ text: '桥西区', value: '130104', parent: '130100' },
{ text: '新华区', value: '130105', parent: '130100' },
{ text: '路南区', value: '130202', parent: '130200' },
{ text: '路北区', value: '130203', parent: '130200' },
{ text: '古冶区', value: '130204', parent: '130200' },
{ text: '海港区', value: '130302', parent: '130300' },
{ text: '山海关区', value: '130303', parent: '130300' },
{ text: '北戴河区', value: '130304', parent: '130300' },
];
/** 模拟从后台查询数据 */
function requestMockData(parent) {
return new Promise((resolve) => {
let data = mockData.filter((i) => i.parent === parent);
setTimeout(() => resolve(data), 500);
});
}
/** 查询后台真实数据 */
async function requestMenu(parent) {
let result;
// 如果parent为空则查询第一级菜单
if (parent === '') {
result = await defHttp.get({
url: '/sys/permission/getSystemMenuList',
params: {},
});
} else {
result = await defHttp.get({
url: '/sys/permission/getSystemSubmenu',
params: { parentId: parent },
});
}
// 返回的数据里必须包含 value 和 text 字段
return result.map((item) => ({ value: item.id, text: item.name }));
}
</script>

View File

@ -0,0 +1,129 @@
<template>
<div>
<b>键盘操作快捷键</b>
<div style="border: 1px solid #cccccc; padding: 8px; width: 740px">
<pre>
F2 | 如果存在激活单元格为编辑状态
Esc | 如果存在取消单元格编辑状态
| 如果存在则移动到上面的单元格
| 如果存在则移动到下面的单元格
| 如果存在则移动到左边的单元格
| 如果存在则移动到右边的单元格
Tab | 如果存在则移动到右边单元格如果移动到最后一列则从下一行开始移到以此循环
Shift + Tab | 如果存在则移动到左边单元格如果移动到第一列则从上一行开始移到以此循环
Enter | 如果存在取消单元格编辑并移动到下面的单元格
Shift + Enter | 如果存在取消单元格编辑并移动到上面的单元格</pre
>
</div>
<JVxeTable ref="tableRef" stripe toolbar rowNumber rowSelection keyboardEdit :columns="columns" :dataSource="dataSource"> </JVxeTable>
</div>
</template>
<script lang="ts">
import { ref, onMounted, nextTick, defineComponent } from 'vue';
import { Popconfirm } from 'ant-design-vue';
import { JVxeTypes, JVxeColumn, JVxeTableInstance } from '/@/components/jeecg/JVxeTable/types';
export default defineComponent({
name: 'JVxeDemo5',
components: { [Popconfirm.name]: Popconfirm },
setup() {
const tableRef = ref<JVxeTableInstance>();
const columns = ref<JVxeColumn[]>([
{
title: '单行文本',
key: 'input',
type: JVxeTypes.input,
width: 220,
defaultValue: '',
placeholder: '请输入${title}',
},
{
title: '多行文本',
key: 'textarea',
type: JVxeTypes.textarea,
width: 240,
},
{
title: '数字',
key: 'number',
type: JVxeTypes.inputNumber,
width: 120,
defaultValue: 32,
},
{
title: '日期时间',
key: 'datetime',
type: JVxeTypes.datetime,
width: 240,
defaultValue: '2019-04-30 14:51:22',
placeholder: '请选择',
},
{
title: '时间',
key: 'time',
type: JVxeTypes.time,
width: 220,
defaultValue: '14:52:22',
placeholder: '请选择',
},
{
title: '下拉框',
key: 'select',
type: JVxeTypes.select,
width: 220,
// 下拉选项
options: [
{ title: 'String', value: 'string' },
{ title: 'Integer', value: 'int' },
{ title: 'Double', value: 'double' },
{ title: 'Boolean', value: 'boolean' },
],
// allowInput: true,
allowSearch: true,
placeholder: '请选择',
},
{
title: '复选框',
key: 'checkbox',
type: JVxeTypes.checkbox,
// width: 100,
customValue: ['Y', 'N'], // true ,false
defaultChecked: false,
},
]);
const dataSource = ref([]);
function handleView(props) {
// 参数介绍:
// props.value 当前单元格的值
// props.row 当前行的数据
// props.rowId 当前行ID
// props.rowIndex 当前行下标
// props.column 当前列的配置
// props.columnIndex 当前列下标
// props.$table vxe-table实例可以调用vxe-table内置方法
// props.scrolling 是否正在滚动
// props.reloadEffect 是否开启了数据刷新特效
// props.triggerChange 触发change事件用于更改slot的值
console.log('props: ', props);
}
function handleDelete({ row }) {
// 使用实例:删除当前操作的行
tableRef.value?.removeRows(row);
}
onMounted(async () => {
console.log(tableRef.value);
await nextTick();
// 默认添加五行数据
tableRef.value!.addRows([{ input: 'input_1' }, { input: 'input_2' }, { input: 'input_3' }, { input: 'input_4' }, { input: 'input_5' }], {
setActive: false,
});
});
return { tableRef, columns, dataSource, handleView, handleDelete };
},
});
</script>

View File

@ -0,0 +1,224 @@
<template>
<a-card title="即时保存示例" :bordered="false">
<!--
即时保存大体思路
1. JVxeTable 上必须加 keep-source 属性
2. 监听 edit-closed事件这个事件是在编辑完成后触发
3. 在这个事件里面判断数据是否更改如果更改了就调用接口进行保存操作
-->
<JVxeTable
toolbar
:toolbarConfig="toolbarConfig"
rowNumber
rowSelection
keepSource
asyncRemove
:height="340"
:loading="loading"
:columns="columns"
:dataSource="dataSource"
:pagination="pagination"
@save="handleTableSave"
@removed="handleTableRemove"
@edit-closed="handleEditClosed"
@pageChange="handlePageChange"
@selectRowChange="handleSelectRowChange"
/>
</a-card>
</template>
<script lang="ts" setup>
// 即时保存示例
import { reactive, ref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { JVxeColumn, JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
import { useMessage } from '/@/hooks/web/useMessage';
const { createMessage } = useMessage();
// 工具栏的按钮配置
const toolbarConfig = reactive({
// add 新增按钮remove 删除按钮clearSelection 清空选择按钮
btn: ['add', 'save', 'remove', 'clearSelection'],
});
// 是否正在加载
const loading = ref(false);
// 分页器参数
const pagination = reactive({
// 当前页码
current: 1,
// 每页的条数
pageSize: 200,
// 可切换的条数
pageSizeOptions: ['10', '20', '30', '100', '200'],
// 数据总数目前并不知道真实的总数所以先填写0在后台查出来后再赋值
total: 0,
});
// 选择的行
const selectedRows = ref<Recordable[]>([]);
// 数据源,控制表格的数据
const dataSource = ref<Recordable[]>([]);
// 列配置,控制表格显示的列
const columns = ref<JVxeColumn[]>([
{ key: 'num', title: '序号', width: 80, type: JVxeTypes.normal },
{
// 字段key跟后台数据的字段名匹配
key: 'ship_name',
// 列的标题
title: '船名',
// 列的宽度
width: 180,
// 如果加上了该属性就代表当前单元格是可编辑的type就是表单的类型input就是简单的输入框
type: JVxeTypes.input,
},
{ key: 'call', title: '呼叫', width: 80, type: JVxeTypes.input },
{ key: 'len', title: '长', width: 80, type: JVxeTypes.input },
{ key: 'ton', title: '吨', width: 120, defaultValue: 233, type: JVxeTypes.input },
{ key: 'payer', title: '付款方', width: 120, defaultValue: '张三', type: JVxeTypes.input },
{ key: 'count', title: '数', width: 40, type: JVxeTypes.normal },
{
key: 'company',
title: '公司',
// 最小宽度,与宽度不同的是,这个不是固定的宽度,如果表格有多余的空间,会平均分配给设置了 minWidth 的列
// 如果要做占满表格的列可以这么写
minWidth: 180,
type: JVxeTypes.input,
},
{ key: 'trend', title: '动向', width: 120, type: JVxeTypes.input },
]);
// 查询url地址
enum Api {
getData = '/mock/vxe/getData',
// 模拟保存单行数据(即时保存)
saveRow = '/mock/vxe/immediateSaveRow',
// 模拟保存整个表格的数据
saveAll = '/mock/vxe/immediateSaveAll',
}
loadData();
// 加载数据
async function loadData() {
loading.value = true;
// 调用查询数据接口
await defHttp
.get({
// 请求地址
url: Api.getData,
// 封装查询条件
params: {
pageNo: pagination.current,
pageSize: pagination.pageSize,
},
})
.then((result) => {
// 后台查询回来的 total数据总数量
pagination.total = result.total;
// 将查询的数据赋值给 dataSource
dataSource.value = result.records;
// 重置选择
selectedRows.value = [];
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
loading.value = false;
});
}
// 【整体保存】点击保存按钮时触发的事件
function handleTableSave({ $table, target }) {
// 校验整个表格
$table.validate().then((errMap) => {
// 校验通过
if (!errMap) {
// 获取所有数据
let tableData = target.getTableData();
console.log('当前保存的数据是', tableData);
// 获取新增的数据
let newData = target.getNewData();
console.log('-- 新增的数据', newData);
// 获取删除的数据
let deleteData = target.getDeleteData();
console.log('-- 删除的数据', deleteData);
// 【模拟保存】
loading.value = true;
defHttp
.post({
url: Api.saveAll,
params: tableData,
})
.then(() => {
createMessage.success(`保存成功`);
})
.finally(() => {
loading.value = false;
});
}
});
}
// 触发单元格删除事件
function handleTableRemove(event) {
// 把 event.deleteRows 传给后台进行删除(注意:这里不会传递前端逻辑新增的数据,因为不需要请求后台删除)
console.log('待删除的数据: ', event.deleteRows);
// 也可以只传ID因为可以根据ID删除
let deleteIds = event.deleteRows.map((row) => row.id);
console.log('待删除的数据ids: ', deleteIds);
// 模拟请求后台删除
loading.value = true;
window.setTimeout(() => {
loading.value = false;
createMessage.success('删除成功');
// 假设后台返回删除成功,必须要调用 confirmRemove() 方法,才会真正在表格里移除(会同时删除选中的逻辑新增的数据)
event.confirmRemove();
}, 1000);
}
// 单元格编辑完成之后触发的事件
function handleEditClosed(event) {
let { $table, row, column } = event;
let field = column.property;
// 判断单元格值是否被修改
if ($table.isUpdateByRow(row, field)) {
// 校验当前行
$table.validate(row).then((errMap) => {
// 校验通过
if (!errMap) {
// 【模拟保存】
let hideLoading = createMessage.loading(`正在保存"${column.title}"`, 0);
console.log('即时保存数据', row);
defHttp
.put({
url: Api.saveRow,
params: row,
})
.then((res) => {
createMessage.success(`"${column.title}"保存成功`);
// 局部更新单元格为已保存状态
$table.reloadRow(row, null, field);
})
.finally(() => {
hideLoading();
});
}
});
}
}
// 当分页参数变化时触发的事件
function handlePageChange(event) {
// 重新赋值
pagination.current = event.current;
pagination.pageSize = event.pageSize;
// 查询数据
loadData();
}
// 当选择的行变化时触发的事件
function handleSelectRowChange(event) {
selectedRows.value = event.selectedRows;
}
</script>
<style scoped></style>

View File

@ -0,0 +1,244 @@
<template>
<a-card title="弹出子表示例" :bordered="false">
<!--
弹出子表大体思路
1. 必须要有 clickRowShowSubForm 属性如果该属性设为false那么就不会弹出子表
2. 必须要有 subForm 插槽用于规定弹出子表的内容
3. highlightCurrentRow 属性可有可无如果有则点击一行的时候该行会背景色会常亮
-->
<!--
弹出详细信息大体思路
1. 必须要有 clickRowShowMainForm 属性如果该属性设为false那么就不会弹出详细信息
2. 必须要有 mainForm 插槽用于规定弹出的内容
-->
<JVxeTable
toolbar
rowNumber
rowSelection
highlightCurrentRow
clickRowShowSubForm
clickRowShowMainForm
:height="750"
:loading="loading"
:columns="columns"
:dataSource="dataSource"
@detailsConfirm="handleDetailsConfirm"
>
<!-- 主表单 -->
<template #mainForm="{ row }">
<template v-if="row">
<a-form ref="form2" :model="row" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="8">
<a-col :span="8">
<a-form-item label="ID" name="id">
<a-input v-model:value="row.id" disabled />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="序号" name="num">
<a-input v-model:value="row.num" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="船名" name="ship_name">
<a-input v-model:value="row.ship_name" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="呼叫" name="call">
<a-input v-model:value="row.call" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="" name="len">
<a-input v-model:value="row.len" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="" name="ton">
<a-input v-model:value="row.ton" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="付款方" name="payer">
<a-input v-model:value="row.payer" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="" name="count">
<a-input v-model:value="row.count" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="公司" name="company">
<a-input v-model:value="row.company" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="动向" name="trend">
<a-input v-model:value="row.trend" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</template>
</template>
<!-- 子表单 -->
<template #subForm="{ row }">
<template v-if="loadSubData(row)">
<JVxeTable
ref="subFormTable"
height="auto"
:max-height="350"
:loading="subTable.loading"
:columns="subTable.columns"
:dataSource="subTable.dataSource"
/>
</template>
</template>
</JVxeTable>
</a-card>
</template>
<script lang="ts" setup>
// 弹出子表示例
import { reactive, ref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { JVxeColumn, JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
import { useMessage } from '/@/hooks/web/useMessage';
const { createMessage } = useMessage();
const loading = ref(false);
const dataSource = ref([]);
const columns = ref<JVxeColumn[]>([
{ key: 'num', title: '序号', width: '80px' },
{ key: 'ship_name', title: '船名', width: '180px', type: JVxeTypes.input },
{ key: 'call', title: '呼叫', width: '80px' },
{ key: 'len', title: '长', width: '80px' },
{ key: 'ton', title: '吨', width: '120px' },
{ key: 'payer', title: '付款方', width: '120px' },
{ key: 'count', title: '数', width: '40px' },
{
key: 'company',
title: '公司',
minWidth: '180px',
// 是否点击显示详细信息
// 只有当前单元格不能编辑的时候才能生效
// 如果不设的话,点击就只弹出子表,不会弹出主表的详细信息
showDetails: true,
},
{ key: 'trend', title: '动向', width: '120px' },
]);
const selectedRows = ref([]);
// 子表的信息
const subTable = reactive({
currentRowId: null,
loading: false,
pagination: { current: 1, pageSize: 200, pageSizeOptions: ['100', '200'], total: 0 },
selectedRows: [],
dataSource: [],
columns: [
{ key: 'dd_num', title: '调度序号', width: '120px' },
{ key: 'tug', title: '拖轮', width: '180px', type: JVxeTypes.input },
{ key: 'work_start_time', title: '作业开始时间', width: '180px', type: JVxeTypes.input },
{ key: 'work_stop_time', title: '作业结束时间', width: '180px', type: JVxeTypes.input },
{ key: 'type', title: '船舶分类', width: '120px', type: JVxeTypes.input },
{ key: 'port_area', title: '所属港区', minWidth: '120px', type: JVxeTypes.input },
] as JVxeColumn[],
});
// form表单 col
const labelCol = reactive({ span: 4 });
const wrapperCol = reactive({ span: 20 });
const rules = reactive({
num: [{ required: true, message: '必须输入序号' }],
});
// 查询url地址
enum Api {
getData = '/mock/vxe/getData',
}
loadData();
// 加载数据
function loadData() {
// 封装查询条件
// 调用查询数据接口
loading.value = true;
defHttp
.get({
url: Api.getData,
params: {
pageNo: 1,
pageSize: 30,
},
})
.then((result) => {
// 将查询的数据赋值给 dataSource
dataSource.value = result.records;
// 重置选择
selectedRows.value = [];
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
loading.value = false;
});
}
// 查询子表数据
function loadSubData(row) {
if (row) {
// 这里一定要做限制,限制不能重复查询,否者会出现死循环
if (subTable.currentRowId === row.id) {
return true;
}
subTable.currentRowId = row.id;
subTable.loading = true;
defHttp
.get({
url: Api.getData,
params: {
pageNo: 1,
pageSize: 30,
parentId: row.id,
},
})
.then((result) => {
// 将查询的数据赋值给 dataSource
subTable.dataSource = result.records;
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
subTable.loading = false;
});
return true;
} else {
return false;
}
}
// 详细信息里点了确认按钮
function handleDetailsConfirm({ row, $table, callback }) {
console.log('保存的数据', row);
// 校验当前行
$table.validate(row).then((errMap) => {
// 校验通过
if (!errMap) {
// 校验子表,如果需要的话,可以操作下面这个对象:
callback(true);
loading.value = true;
setTimeout(() => {
loading.value = false;
createMessage.success('保存成功');
}, 1000);
} else {
callback(false);
createMessage.warn('校验失败');
}
});
}
</script>
<style scoped></style>

View File

@ -0,0 +1,126 @@
<template>
<a-card title="无痕刷新示例" :bordered="false">
<div style="margin-bottom: 8px">
<span>启用数据变动特效</span>
<a-switch v-model:checked="reloadEffect" />
</div>
<!--
无痕刷新大体思路
1. 该功能依赖于即时保存功能请先看即时保存示例
2. 必须要有 socket-reload 属性且设为 true
3. 必须要有 socket-key 属性该属性为当前表格的唯一标识
系统会自动更新所有 socket-key 相同的表格
4. 在局部保存 edit-closed 事件中
保存成功后调用 socketSendUpdateRow 方法将当前 row 传递过去即可 见第 102
-->
<JVxeTable
ref="tableRef"
rowNumber
rowSelection
keepSource
socketReload
socketKey="demo-socket-reload"
:reloadEffect="reloadEffect"
:height="340"
:loading="loading"
:columns="columns"
:dataSource="dataSource"
@valueChange="onValueChange"
@edit-closed="handleEditClosed"
/>
</a-card>
</template>
<script lang="ts" setup>
// 无痕刷新示例
import { ref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { JVxeColumn, JVxeTableInstance, JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
import { useMessage } from '/@/hooks/web/useMessage';
const { createMessage } = useMessage();
const tableRef = ref<JVxeTableInstance>();
// 是否启用日历刷新效果
const reloadEffect = ref(true);
const loading = ref(false);
const dataSource = ref<Recordable[]>([]);
const columns = ref<JVxeColumn[]>([
{ key: 'num', title: '序号', width: 80 },
{ key: 'enabled', title: '启用', width: 80, type: JVxeTypes.checkbox },
{ key: 'ship_name', title: '船名', width: 180, type: JVxeTypes.input },
{ key: 'call', title: '呼叫', width: 80, type: JVxeTypes.input },
{ key: 'len', title: '长', width: 80, type: JVxeTypes.input },
{ key: 'ton', title: '吨', width: 120, type: JVxeTypes.input },
{ key: 'payer', title: '付款方', width: 120, type: JVxeTypes.input },
{ key: 'count', title: '数', width: 40 },
{ key: 'company', title: '公司', minWidth: 180, type: JVxeTypes.input },
{ key: 'trend', title: '动向', width: 120, type: JVxeTypes.input },
]);
// 查询url地址
enum Api {
getData = '/mock/vxe/getData',
}
loadData();
// 加载数据
function loadData() {
loading.value = true;
defHttp
.get({
url: Api.getData,
params: { pageNo: 1, pageSize: 200 },
})
.then((result) => {
dataSource.value = result.records;
})
.finally(() => {
loading.value = false;
});
}
/** 单元格值变化时触发的事件 */
function onValueChange(event) {
switch (event.type) {
// 所有不能触发 editClosed 事件的组件都需要定义在这里可以安装你自己的业务需求来完善此处的case
case JVxeTypes.radio:
case JVxeTypes.checkbox:
doSendUpdateRow(event);
break;
}
}
// 单元格编辑完成之后触发的事件
function handleEditClosed(event) {
doSendUpdateRow(event);
}
// 发送变更行请求
function doSendUpdateRow(event) {
let { $table, row, column } = event;
let field = column.property;
// 判断单元格值是否被修改
if ($table.isUpdateByRow(row, field)) {
// 校验当前行
$table.validate(row).then((errMap) => {
// 校验通过
if (!errMap) {
// 【模拟保存】(此处需要替换成真实的请求)
let hideLoading = createMessage.loading(`正在保存"${column.title}"`, 0);
setTimeout(() => {
hideLoading();
createMessage.success(`"${column.title}"保存成功`);
// 局部更新单元格为已保存状态
$table.reloadRow(row, null, field);
// 发送更新消息
tableRef.value?.socketSendUpdateRow(row);
}, 555);
}
});
}
}
</script>
<style scoped></style>

View File

@ -0,0 +1,40 @@
<template>
<PageWrapper>
<a-card :bordered="false">
<template #title>
<span>
JVXETable是专门为大数据和各种ERP风格的复杂操作研发的的高性能表格组件底层采用vxe-table组件可以完美弥补antd默认table性能不足问题
<a href="https://help.jeecg.com/component/JVxeTable.html">API文档</a>
</span>
</template>
<a-tabs defaultActiveKey="1">
<a-tab-pane tab="基础示例" key="1">
<JVxeDemo1 />
</a-tab-pane>
<a-tab-pane tab="高级示例" key="2">
<JVxeDemo2 />
</a-tab-pane>
<a-tab-pane tab="排序示例" key="3">
<JVxeDemo3 />
</a-tab-pane>
<a-tab-pane tab="联动示例" key="4">
<JVxeDemo4 />
</a-tab-pane>
<a-tab-pane tab="键盘操作" key="5">
<JVxeDemo5 />
</a-tab-pane>
</a-tabs>
</a-card>
</PageWrapper>
</template>
<script lang="ts" setup>
// noinspection ES6UnusedImports
import { PageWrapper } from '/@/components/Page';
import JVxeDemo1 from './JVxeDemo1.vue';
import JVxeDemo2 from './JVxeDemo2.vue';
import JVxeDemo3 from './JVxeDemo3.vue';
import JVxeDemo4 from './JVxeDemo4.vue';
import JVxeDemo5 from './JVxeDemo5.vue';
</script>

View File

@ -0,0 +1,319 @@
<template>
<a-card :bordered="false">
<JVxeTable
toolbar
:toolbarConfig="toolbarConfig"
rowNumber
rowSelection
rowSelectionType="radio"
clickSelectRow
highlightCurrentRow
:height="tableHeight"
:loading="table1.loading"
:columns="table1.columns"
:dataSource="table1.dataSource"
:pagination="table1.pagination"
:expandConfig="expandConfig"
style="margin-bottom: 8px"
@pageChange="handleTable1PageChange"
@selectRowChange="handleTable1SelectRowChange"
></JVxeTable>
<a-tabs v-show="subTabs.show" :class="{ 'sub-tabs': true, 'un-expand': !subTabs.expand }">
<a-tab-pane tab="子表1" key="1">
<JVxeTable
toolbar
row-number
row-selection
height="auto"
:maxHeight="350"
:loading="table2.loading"
:columns="table2.columns"
:dataSource="table2.dataSource"
:pagination="table2.pagination"
@pageChange="handleTable2PageChange"
@selectRowChange="handleTable2SelectRowChange"
/>
</a-tab-pane>
<a-tab-pane tab="子表2" key="2">
<h1>这里是子表2</h1>
<h1>这里是子表2</h1>
<h1>这里是子表2</h1>
<h1>这里是子表2</h1>
<h1>这里是子表2</h1>
<h1>这里是子表2</h1>
</a-tab-pane>
</a-tabs>
</a-card>
</template>
<script>
import { h } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
import { Button, Checkbox } from 'ant-design-vue';
import { UpOutlined, DownOutlined } from '@ant-design/icons-vue';
export default {
name: 'ErpTemplate',
data() {
return {
toolbarConfig: {
// prefix 前缀suffix 后缀
slot: ['prefix', 'suffix'],
// add 新增按钮remove 删除按钮clearSelection 清空选择按钮
btn: ['add', 'remove', 'clearSelection'],
},
expandConfig: {
// 是否只能同时展开一行
accordion: true,
},
// 子表 tabs
subTabs: {
show: false,
// 是否展开
expand: true,
// 是否自动展开
autoExpand: true,
},
table1: {
// 是否正在加载
loading: false,
// 分页器参数
pagination: {
// 当前页码
current: 1,
// 每页的条数
pageSize: 200,
// 可切换的条数
pageSizeOptions: ['10', '20', '30', '100', '200'],
// 数据总数目前并不知道真实的总数所以先填写0在后台查出来后再赋值
total: 0,
showTotal: (total, range) => {
// 此处为 jsx 语法
let text = h('span', `${range[0]}-${range[1]} 共 ${total} 条`);
// 判断子表是否显示,如果显示就渲染展开收起按钮
if (this.subTabs.show) {
let expand = h('span', {}, [
h(
Button,
{
type: 'link',
onClick: this.handleToggleTabs,
},
() => [this.subTabs.expand ? h(UpOutlined) : h(DownOutlined), h('span', {}, this.subTabs.expand ? '收起' : '展开')]
),
h(
Checkbox,
{
// h 写法不支持 v-model , 所以需要手动赋值
checked: this.subTabs.autoExpand,
'onUpdate:checked': (checked) => (this.subTabs.autoExpand = checked),
},
() => '自动展开'
),
]);
// 返回多个dom用数组
return [expand, text];
} else {
// 直接返回单个dom
return text;
}
},
},
// 选择的行
selectedRows: [],
// 数据源,控制表格的数据
dataSource: [],
// 列配置,控制表格显示的列
columns: [
{ key: 'num', title: '序号', width: '80px' },
{
// 字段key跟后台数据的字段名匹配
key: 'ship_name',
// 列的标题
title: '船名',
// 列的宽度
width: '180px',
// 如果加上了该属性就代表当前单元格是可编辑的type就是表单的类型input就是简单的输入框
type: JVxeTypes.input,
},
{ key: 'call', title: '呼叫', width: '990px', type: JVxeTypes.input },
{ key: 'len', title: '长', width: '80px', type: JVxeTypes.inputNumber },
{ key: 'ton', title: '吨', width: '120px', type: JVxeTypes.inputNumber },
{ key: 'payer', title: '付款方', width: '120px', type: JVxeTypes.input },
{ key: 'count', title: '数', width: '40px' },
{
key: 'company',
title: '公司',
// 最小宽度,与宽度不同的是,这个不是固定的宽度,如果表格有多余的空间,会平均分配给设置了 minWidth 的列
// 如果要做占满表格的列可以这么写
minWidth: '180px',
type: JVxeTypes.input,
},
{ key: 'trend', title: '动向', width: '120px', type: JVxeTypes.input },
],
},
// 子级表的配置信息 (配置和主表的完全一致,就不写冗余的注释了)
table2: {
currentRowId: null,
loading: false,
pagination: { current: 1, pageSize: 10, pageSizeOptions: ['5', '10', '20', '30'], total: 0 },
selectedRows: [],
dataSource: [],
columns: [
{ key: 'dd_num', title: '调度序号', width: '120px' },
{ key: 'tug', title: '拖轮', width: '180px', type: JVxeTypes.input },
{ key: 'work_start_time', title: '作业开始时间', width: '180px', type: JVxeTypes.input },
{ key: 'work_stop_time', title: '作业结束时间', width: '180px', type: JVxeTypes.input },
{ key: 'type', title: '船舶分类', width: '120px', type: JVxeTypes.input },
{ key: 'port_area', title: '所属港区', width: '120px', type: JVxeTypes.input },
],
},
currentSubRow: null,
// 查询url地址
url: {
getData: '/mock/vxe/getData',
},
};
},
computed: {
tableHeight() {
let { show, expand } = this.subTabs;
return show ? (expand ? 350 : 482) : 482;
},
},
created() {
this.loadTable1Data();
},
methods: {
// 加载table1【主表】的数据
loadTable1Data() {
// 封装查询条件
let formData = {
pageNo: this.table1.pagination.current,
pageSize: this.table1.pagination.pageSize,
};
// 调用查询数据接口
this.table1.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
// 后台查询回来的 total数据总数量
this.table1.pagination.total = result.total;
// 将查询的数据赋值给 dataSource
this.table1.dataSource = result.records;
// 重置选择
this.table1.selectedRows = [];
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
this.table1.loading = false;
});
},
// 查询子表数据
loadSubData(row) {
if (row) {
// 这里一定要做限制,限制不能重复查询,否者会出现死循环
if (this.table2.currentRowId === row.id) {
return true;
}
this.table2.currentRowId = row.id;
this.loadTable2Data();
return true;
} else {
return false;
}
},
// 查询子表数据
loadTable2Data() {
let table2 = this.table2;
let formData = {
parentId: table2.currentRowId,
pageNo: this.table2.pagination.current,
pageSize: this.table2.pagination.pageSize,
};
table2.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
// 将查询的数据赋值给 dataSource
table2.selectedRows = [];
table2.dataSource = result.records;
table2.pagination.total = result.total;
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
table2.loading = false;
});
},
// table1【主表】当选择的行变化时触发的事件
handleTable1SelectRowChange(event) {
this.table1.selectedRows = event.selectedRows;
this.subTabs.show = true;
if (this.subTabs.autoExpand) {
this.subTabs.expand = true;
}
this.loadSubData(event.selectedRows[0]);
},
// table2【子表】当选择的行变化时触发的事件
handleTable2SelectRowChange(event) {
this.table2.selectedRows = event.selectedRows;
},
handleTable1PageChange(event) {
// 重新赋值
this.table1.pagination.current = event.current;
this.table1.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable1Data();
},
// 当table2【子表】分页参数变化时触发的事件
handleTable2PageChange(event) {
// 重新赋值
this.table2.pagination.current = event.current;
this.table2.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable2Data();
},
// 展开或收起子表tabs
handleToggleTabs() {
this.subTabs.expand = !this.subTabs.expand;
},
},
};
</script>
<style lang="less" scoped>
.sub-tabs {
&.un-expand {
:deep(.ant-tabs-content) {
height: 0 !important;
}
:deep(.ant-tabs-nav) {
border-color: transparent !important;
}
:deep(.ant-tabs-ink-bar) {
background-color: transparent !important;
}
:deep(.ant-tabs-tab) {
display: none !important;
}
}
}
</style>

View File

@ -0,0 +1,332 @@
<template>
<a-card :bordered="false">
<a-row :gutter="8">
<!-- 这里是父级节点 -->
<a-col :span="24" style="margin-bottom: 4px">
<JVxeTable
toolbar
rowNumber
rowSelection
clickSelectRow
highlightCurrentRow
:radioConfig="{ highlight: false }"
:checkboxConfig="{ highlight: false }"
:height="340"
:loading="table1.loading"
:columns="table1.columns"
:dataSource="table1.dataSource"
:pagination="table1.pagination"
@pageChange="handleTable1PageChange"
@selectRowChange="handleTable1SelectRowChange"
/>
</a-col>
<!-- 这里是子级节点 -->
<a-col :span="12">
<JVxeTable
toolbar
rowNumber
rowSelection
clickSelectRow
highlightCurrentRow
:radioConfig="{ highlight: false }"
:checkboxConfig="{ highlight: false }"
:height="340"
:loading="table2.loading"
:columns="table2.columns"
:dataSource="table2.dataSource"
:pagination="table2.pagination"
@pageChange="handleTable2PageChange"
@selectRowChange="handleTable2SelectRowChange"
>
</JVxeTable>
</a-col>
<!-- 这里是孙级节点 -->
<a-col :span="12">
<JVxeTable
toolbar
rowNumber
rowSelection
:height="340"
:loading="table3.loading"
:columns="table3.columns"
:dataSource="table3.dataSource"
:pagination="table3.pagination"
@pageChange="handleTable3PageChange"
>
</JVxeTable>
</a-col>
</a-row>
</a-card>
</template>
<script>
import { defHttp } from '/@/utils/http/axios';
import { JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
// 【多种布局模板】上面父、左下子、右下孙
export default {
name: 'Template1',
data() {
return {
table1: {
// 是否正在加载
loading: false,
// 分页器参数
pagination: {
// 当前页码
current: 1,
// 每页的条数
pageSize: 200,
// 可切换的条数
pageSizeOptions: ['10', '20', '30', '100', '200'],
// 数据总数目前并不知道真实的总数所以先填写0在后台查出来后再赋值
total: 0,
},
// 最后选中的行
lastRow: null,
// 选择的行
selectedRows: [],
// 数据源,控制表格的数据
dataSource: [],
// 列配置,控制表格显示的列
columns: [
{ key: 'num', title: '序号', width: '80px' },
{
// 字段key跟后台数据的字段名匹配
key: 'ship_name',
// 列的标题
title: '船名',
// 列的宽度
width: '180px',
// 如果加上了该属性就代表当前单元格是可编辑的type就是表单的类型input就是简单的输入框
type: JVxeTypes.input,
formatter({ cellValue, row, column }) {
let foo = '';
if (row.company === '佧伒侾佯有限公司') {
foo += '-233';
}
return cellValue + foo;
},
},
{ key: 'call', title: '呼叫', width: '80px', type: JVxeTypes.input },
{ key: 'len', title: '长', width: '80px', type: JVxeTypes.inputNumber },
{ key: 'ton', title: '吨', width: '120px', type: JVxeTypes.inputNumber },
{ key: 'payer', title: '付款方', width: '120px', type: JVxeTypes.input },
{ key: 'count', title: '数', width: '40px' },
{
key: 'company',
title: '公司',
// 最小宽度,与宽度不同的是,这个不是固定的宽度,如果表格有多余的空间,会平均分配给设置了 minWidth 的列
// 如果要做占满表格的列可以这么写
minWidth: '180px',
type: JVxeTypes.input,
},
{ key: 'trend', title: '动向', width: '120px', type: JVxeTypes.input },
],
},
// 子级表的配置信息 (配置和主表的完全一致,就不写冗余的注释了)
table2: {
loading: false,
pagination: { current: 1, pageSize: 200, pageSizeOptions: ['100', '200'], total: 0 },
// 最后选中的行
lastRow: null,
selectedRows: [],
dataSource: [],
columns: [
{ key: 'dd_num', title: '调度序号', width: '120px' },
{ key: 'tug', title: '拖轮', width: '180px', type: JVxeTypes.input },
{ key: 'work_start_time', title: '作业开始时间', width: '180px', type: JVxeTypes.input },
{ key: 'work_stop_time', title: '作业结束时间', width: '180px', type: JVxeTypes.input },
{ key: 'type', title: '船舶分类', width: '120px', type: JVxeTypes.input },
{ key: 'port_area', title: '所属港区', width: '120px', type: JVxeTypes.input },
],
},
// 孙级表的配置信息 (配置和主表的完全一致,就不写冗余的注释了)
table3: {
loading: false,
pagination: { current: 1, pageSize: 200, pageSizeOptions: ['100', '200'], total: 0 },
selectedRows: [],
dataSource: [],
columns: [
{ key: 'dd_num', title: '调度序号', width: '120px' },
{ key: 'tug', title: '拖轮', width: '120px', type: JVxeTypes.input },
{ key: 'power', title: '马力', width: '120px', type: JVxeTypes.input },
{ key: 'nature', title: '性质', width: '120px', type: JVxeTypes.input },
{ key: 'departure_time', title: '发船时间', width: '180px', type: JVxeTypes.input },
],
},
// 查询url地址
url: {
getData: '/mock/vxe/getData',
},
};
},
// 监听器
watch: {
// 监听table1 【主表】选择的数据发生了变化
['table1.lastRow'](row) {
this.loadTable2Data();
},
// 监听table2 【子表】选择的数据发生了变化
['table2.lastRow']() {
this.loadTable3Data();
},
},
created() {
this.loadTable1Data();
},
methods: {
// 加载table1【主表】的数据
loadTable1Data() {
// 封装查询条件
let formData = {
pageNo: this.table1.pagination.current,
pageSize: this.table1.pagination.pageSize,
};
// 调用查询数据接口
this.table1.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
// 后台查询回来的 total数据总数量
this.table1.pagination.total = result.total;
// 将查询的数据赋值给 dataSource
this.table1.dataSource = result.records;
// 重置选择
this.table1.selectedRows = [];
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
this.table1.loading = false;
});
},
// 当table1【主表】分页参数变化时触发的事件
handleTable1PageChange(event) {
// 重新赋值
this.table1.pagination.current = event.current;
this.table1.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable1Data();
},
// table1【主表】当选择的行变化时触发的事件
handleTable1SelectRowChange(event) {
this.handleTableSelectRowChange(this.table1, event);
},
// 加载table2【子表】的数据根据主表的id进行查询
loadTable2Data() {
// 如果主表没有选择,则不查询
let selectedRows = this.table1.selectedRows;
if (!selectedRows || selectedRows.length === 0) {
this.table2.pagination.total = 0;
this.table2.dataSource = [];
this.table2.selectedRows = [];
return;
} else if (this.table1.lastRow == null) {
this.table1.lastRow = selectedRows[selectedRows.length - 1];
}
let formData = {
parentId: this.table1.lastRow.id,
pageNo: this.table2.pagination.current,
pageSize: this.table2.pagination.pageSize,
};
this.table2.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
this.table2.pagination.total = result.total;
this.table2.dataSource = result.records;
this.table2.selectedRows = [];
})
.finally(() => {
this.table2.loading = false;
});
},
// table2【子表】当选择的行变化时触发的事件
handleTable2SelectRowChange(event) {
this.handleTableSelectRowChange(this.table2, event);
},
// 当table2【子表】分页参数变化时触发的事件
handleTable2PageChange(event) {
// 重新赋值
this.table2.pagination.current = event.current;
this.table2.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable2Data();
},
// 加载table3【孙表】的数据根据子表的id进行查询
loadTable3Data() {
// 如果主表没有选择,则不查询
let selectedRows = this.table2.selectedRows;
if (!selectedRows || selectedRows.length === 0) {
this.table3.pagination.total = 0;
this.table3.dataSource = [];
this.table3.selectedRows = [];
return;
} else if (this.table2.lastRow == null) {
this.table2.lastRow = selectedRows[selectedRows.length - 1];
}
let formData = {
parentId: this.table2.lastRow.id,
pageNo: this.table3.pagination.current,
pageSize: this.table3.pagination.pageSize,
};
this.table3.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
this.table3.pagination.total = result.total;
this.table3.dataSource = result.records;
})
.finally(() => {
this.table3.loading = false;
});
},
// 当table3【孙表】分页参数变化时触发的事件
handleTable3PageChange(event) {
// 重新赋值
this.table3.pagination.current = event.current;
this.table3.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable3Data();
},
/** 公共方法:处理表格选中变化事件 */
handleTableSelectRowChange(table, event) {
let { row, action, selectedRows, $table } = event;
// 获取最后一个选中的
let lastSelected = selectedRows[selectedRows.length - 1];
if (action === 'selected') {
table.lastRow = row;
} else if (action === 'selected-all') {
// 取消全选
if (selectedRows.length === 0) {
table.lastRow = null;
} else if (!table.lastRow) {
table.lastRow = lastSelected;
}
} else if (action === 'unselected' && row === table.lastRow) {
table.lastRow = lastSelected;
}
$table.setCurrentRow(table.lastRow);
table.selectedRows = selectedRows;
},
},
};
</script>
<style lang="less"></style>

View File

@ -0,0 +1,249 @@
<template>
<a-card :bordered="false">
<a-row :gutter="8">
<!-- 左侧父 -->
<a-col :span="12">
<JVxeTable
toolbar
rowNumber
rowSelection
clickSelectRow
highlightCurrentRow
:radioConfig="{ highlight: false }"
:checkboxConfig="{ highlight: false }"
:height="790"
:loading="table1.loading"
:columns="table1.columns"
:dataSource="table1.dataSource"
:pagination="table1.pagination"
@pageChange="handleTable1PageChange"
@selectRowChange="handleTable1SelectRowChange"
/>
</a-col>
<a-col :span="12">
<!-- 左侧选择的数据展示在这里 -->
<JVxeTable rowNumber :height="375" :columns="table1.columns" :dataSource="table1.selectedRows" style="margin: 52px 0 8px" />
<!-- 右下子 -->
<JVxeTable
toolbar
rowNumber
rowSelection
clickSelectRow
:height="355"
:loading="table2.loading"
:columns="table2.columns"
:dataSource="table2.dataSource"
:pagination="table2.pagination"
@pageChange="handleTable2PageChange"
@selectRowChange="handleTable2SelectRowChange"
/>
</a-col>
</a-row>
</a-card>
</template>
<script>
import { defHttp } from '/@/utils/http/axios';
import { JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
// 【多种布局模板】 左边选择后,记录选到右侧,右侧是父、子
export default {
name: 'Template2',
data() {
return {
table1: {
// 是否正在加载
loading: false,
// 分页器参数
pagination: {
// 当前页码
current: 1,
// 每页的条数
pageSize: 200,
// 可切换的条数
pageSizeOptions: ['10', '20', '30', '100', '200'],
// 数据总数目前并不知道真实的总数所以先填写0在后台查出来后再赋值
total: 0,
},
// 最后选中的行
lastRow: null,
// 选择的行
selectedRows: [],
// 数据源,控制表格的数据
dataSource: [],
// 列配置,控制表格显示的列
columns: [
{ key: 'num', title: '序号', width: '80px' },
{
// 字段key跟后台数据的字段名匹配
key: 'ship_name',
// 列的标题
title: '船名',
// 列的宽度
width: '180px',
// 如果加上了该属性就代表当前单元格是可编辑的type就是表单的类型input就是简单的输入框
type: JVxeTypes.input,
},
{ key: 'call', title: '呼叫', width: '80px', type: JVxeTypes.input },
{ key: 'len', title: '长', width: '80px', type: JVxeTypes.input },
{ key: 'ton', title: '吨', width: '120px', type: JVxeTypes.input },
{ key: 'payer', title: '付款方', width: '120px', type: JVxeTypes.input },
{ key: 'count', title: '数', width: '40px' },
{
key: 'company',
title: '公司',
// 最小宽度,与宽度不同的是,这个不是固定的宽度,如果表格有多余的空间,会平均分配给设置了 minWidth 的列
// 如果要做占满表格的列可以这么写
minWidth: '180px',
type: JVxeTypes.input,
},
{ key: 'trend', title: '动向', width: '120px', type: JVxeTypes.input },
],
},
// 子级表的配置信息 (配置和主表的完全一致,就不写冗余的注释了)
table2: {
loading: false,
pagination: { current: 1, pageSize: 200, pageSizeOptions: ['100', '200'], total: 0 },
selectedRows: [],
dataSource: [],
columns: [
{ key: 'dd_num', title: '调度序号', width: '120px' },
{ key: 'tug', title: '拖轮', width: '180px', type: JVxeTypes.input },
{ key: 'work_start_time', title: '作业开始时间', width: '180px', type: JVxeTypes.input },
{ key: 'work_stop_time', title: '作业结束时间', width: '180px', type: JVxeTypes.input },
{ key: 'type', title: '船舶分类', width: '120px', type: JVxeTypes.input },
{ key: 'port_area', title: '所属港区', width: '120px', type: JVxeTypes.input },
],
},
// 查询url地址
url: {
getData: '/mock/vxe/getData',
},
};
},
// 监听器
watch: {
// 监听table1 【主表】选择的数据发生了变化
['table1.lastRow']() {
this.loadTable2Data();
},
},
created() {
this.loadTable1Data();
},
methods: {
// 加载table1【主表】的数据
loadTable1Data() {
// 封装查询条件
let formData = {
pageNo: this.table1.pagination.current,
pageSize: this.table1.pagination.pageSize,
};
// 调用查询数据接口
this.table1.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
// 后台查询回来的 total数据总数量
this.table1.pagination.total = result.total;
// 将查询的数据赋值给 dataSource
this.table1.dataSource = result.records;
// 重置选择
this.table1.selectedRows = [];
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
this.table1.loading = false;
});
},
// 加载table2【子表】的数据根据主表的id进行查询
loadTable2Data() {
// 如果主表没有选择,则不查询
let selectedRows = this.table1.selectedRows;
if (!selectedRows || selectedRows.length === 0) {
this.table2.pagination.total = 0;
this.table2.dataSource = [];
this.table2.selectedRows = [];
return;
} else if (this.table1.lastRow == null) {
this.table1.lastRow = selectedRows[selectedRows.length - 1];
}
let formData = {
parentId: this.table1.lastRow.id,
pageNo: this.table2.pagination.current,
pageSize: this.table2.pagination.pageSize,
};
this.table2.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
this.table2.pagination.total = result.total;
this.table2.dataSource = result.records;
this.table2.selectedRows = [];
})
.finally(() => {
this.table2.loading = false;
});
},
// table1【主表】当选择的行变化时触发的事件
handleTable1SelectRowChange(event) {
this.handleTableSelectRowChange(this.table1, event);
},
// table2【子表】当选择的行变化时触发的事件
handleTable2SelectRowChange(event) {
this.table2.selectedRows = event.selectedRows;
},
// 当table1【主表】分页参数变化时触发的事件
handleTable1PageChange(event) {
// 重新赋值
this.table1.pagination.current = event.current;
this.table1.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable1Data();
},
// 当table2【子表】分页参数变化时触发的事件
handleTable2PageChange(event) {
// 重新赋值
this.table2.pagination.current = event.current;
this.table2.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable2Data();
},
/** 公共方法:处理表格选中变化事件 */
handleTableSelectRowChange(table, event) {
let { row, action, selectedRows, $table } = event;
// 获取最后一个选中的
let lastSelected = selectedRows[selectedRows.length - 1];
if (action === 'selected') {
table.lastRow = row;
} else if (action === 'selected-all') {
// 取消全选
if (selectedRows.length === 0) {
table.lastRow = null;
} else if (!table.lastRow) {
table.lastRow = lastSelected;
}
} else if (action === 'unselected' && row === table.lastRow) {
table.lastRow = lastSelected;
}
$table.setCurrentRow(table.lastRow);
table.selectedRows = selectedRows;
},
},
};
</script>
<style scoped></style>

View File

@ -0,0 +1,237 @@
<template>
<a-card :bordered="false">
<a-row :gutter="8">
<a-col :span="12">
<!-- 左上父 -->
<JVxeTable
toolbar
rowNumber
rowSelection
clickSelectRow
highlightCurrentRow
:radioConfig="{ highlight: false }"
:checkboxConfig="{ highlight: false }"
:height="357"
:loading="table1.loading"
:columns="table1.columns"
:dataSource="table1.dataSource"
:pagination="table1.pagination"
style="margin-bottom: 8px"
@pageChange="handleTable1PageChange"
@selectRowChange="handleTable1SelectRowChange"
/>
<!-- 左下子 -->
<JVxeTable
toolbar
rowNumber
rowSelection
clickSelectRow
:height="356"
:loading="table2.loading"
:columns="table2.columns"
:dataSource="table2.dataSource"
:pagination="table2.pagination"
@pageChange="handleTable2PageChange"
/>
</a-col>
<!-- 左侧父选择的数据展示在这里 -->
<a-col :span="12">
<JVxeTable rowNumber :height="812" :columns="table1.columns" :dataSource="table1.selectedRows" style="margin-top: 52px" />
</a-col>
</a-row>
</a-card>
</template>
<script>
import { defHttp } from '/@/utils/http/axios';
import { JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
// 【多种布局模板】左侧上边是主表、下边是子表,右侧是选中数据
export default {
name: 'Template3',
components: {},
data() {
return {
// 主表的配置信息
table1: {
// 是否正在加载
loading: false,
// 分页器参数
pagination: {
// 当前页码
current: 1,
// 每页的条数
pageSize: 200,
// 可切换的条数
pageSizeOptions: ['10', '20', '30', '100', '200'],
// 数据总数目前并不知道真实的总数所以先填写0在后台查出来后再赋值
total: 0,
},
// 最后选中的行
lastRow: null,
// 选择的行
selectedRows: [],
// 数据源,控制表格的数据
dataSource: [],
// 列配置,控制表格显示的列
columns: [
{ key: 'num', title: '序号', width: '80px' },
{
// 字段key跟后台数据的字段名匹配
key: 'ship_name',
// 列的标题
title: '船名',
// 列的宽度
width: '180px',
// 如果加上了该属性就代表当前单元格是可编辑的type就是表单的类型input就是简单的输入框
type: JVxeTypes.input,
},
{ key: 'call', title: '呼叫', width: '80px', type: JVxeTypes.input },
{ key: 'len', title: '长', width: '80px', type: JVxeTypes.input },
{ key: 'ton', title: '吨', width: '120px', type: JVxeTypes.input },
{ key: 'payer', title: '付款方', width: '120px', type: JVxeTypes.input },
{ key: 'count', title: '数', width: '40px' },
{ key: 'company', title: '公司', width: '180px', type: JVxeTypes.input },
{ key: 'trend', title: '动向', width: '120px', type: JVxeTypes.input },
],
},
// 子表的配置信息 (配置和主表的完全一致,就不写冗余的注释了)
table2: {
loading: false,
pagination: { current: 1, pageSize: 200, pageSizeOptions: ['100', '200'], total: 0 },
dataSource: [],
columns: [
{ key: 'dd_num', title: '调度序号', width: '120px' },
{ key: 'tug', title: '拖轮', width: '180px', type: JVxeTypes.input },
{ key: 'work_start_time', title: '作业开始时间', width: '180px', type: JVxeTypes.input },
{ key: 'work_stop_time', title: '作业结束时间', width: '180px', type: JVxeTypes.input },
{ key: 'type', title: '船舶分类', width: '120px', type: JVxeTypes.input },
{ key: 'port_area', title: '所属港区', width: '120px', type: JVxeTypes.input },
],
},
// 查询url地址
url: {
getData: '/mock/vxe/getData',
},
};
},
// 监听器
watch: {
// 监听table1 【主表】选择的数据发生了变化
['table1.lastRow'](row) {
this.loadTable2Data();
},
},
created() {
this.loadTable1Data();
},
methods: {
// 加载table1主表的数据
loadTable1Data() {
// 封装查询条件
let formData = {
pageNo: this.table1.pagination.current,
pageSize: this.table1.pagination.pageSize,
};
// 调用查询数据接口
this.table1.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
// 后台查询回来的 total数据总数量
this.table1.pagination.total = result.total;
// 将查询的数据赋值给 dataSource
this.table1.dataSource = result.records;
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
this.table1.loading = false;
});
},
// 加载table2子表的数据根据主表的id进行查询
loadTable2Data() {
// 如果主表没有选择,则不查询
let selectedRows = this.table1.selectedRows;
if (!selectedRows || selectedRows.length === 0) {
this.table2.pagination.total = 0;
this.table2.dataSource = [];
return;
} else if (this.table1.lastRow == null) {
this.table1.lastRow = selectedRows[selectedRows.length - 1];
}
let formData = {
parentId: this.table1.lastRow.id,
pageNo: this.table2.pagination.current,
pageSize: this.table2.pagination.pageSize,
};
this.table2.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
this.table2.pagination.total = result.total;
this.table2.dataSource = result.records;
})
.finally(() => {
this.table2.loading = false;
});
},
// table1【主表】当分页参数变化时触发的事件
handleTable1PageChange(event) {
// 重新赋值
this.table1.pagination.current = event.current;
this.table1.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable1Data();
// 分页后重置选择
this.table1.selectedRows = [];
this.loadTable2Data();
},
// table2【子表】当分页参数变化时触发的事件
handleTable2PageChange(event) {
// 重新赋值
this.table1.pagination.current = event.current;
this.table1.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable2Data();
},
// table1【主表】当选择的行变化时触发的事件
handleTable1SelectRowChange(event) {
this.handleTableSelectRowChange(this.table1, event);
},
/** 公共方法:处理表格选中变化事件 */
handleTableSelectRowChange(table, event) {
let { row, action, selectedRows, $table } = event;
// 获取最后一个选中的
let lastSelected = selectedRows[selectedRows.length - 1];
if (action === 'selected') {
table.lastRow = row;
} else if (action === 'selected-all') {
// 取消全选
if (selectedRows.length === 0) {
table.lastRow = null;
} else if (!table.lastRow) {
table.lastRow = lastSelected;
}
} else if (action === 'unselected' && row === table.lastRow) {
table.lastRow = lastSelected;
}
$table.setCurrentRow(table.lastRow);
table.selectedRows = selectedRows;
},
},
};
</script>
<style scoped></style>

View File

@ -0,0 +1,340 @@
<template>
<a-card :bordered="false">
<a-row :gutter="8">
<a-col :span="12">
<!-- 左上父 -->
<JVxeTable
toolbar
rowNumber
rowSelection
clickSelectRow
highlightCurrentRow
:radioConfig="{ highlight: false }"
:checkboxConfig="{ highlight: false }"
:height="340"
:loading="table1.loading"
:columns="table1.columns"
:dataSource="table1.dataSource"
:pagination="table1.pagination"
style="margin-bottom: 8px"
@pageChange="handleTable1PageChange"
@selectRowChange="handleTable1SelectRowChange"
/>
<!-- 左下子 -->
<JVxeTable
toolbar
rowNumber
rowSelection
clickSelectRow
:height="350"
:loading="table2.loading"
:columns="table2.columns"
:dataSource="table2.dataSource"
:pagination="table2.pagination"
@pageChange="handleTable2PageChange"
/>
</a-col>
<!-- 左侧父选择的数据展示在这里 -->
<a-col :span="12">
<!-- 右上父 -->
<JVxeTable
rowNumber
rowSelection
clickSelectRow
highlightCurrentRow
:radioConfig="{ highlight: false }"
:checkboxConfig="{ highlight: false }"
:height="340"
:columns="table1.columns"
:dataSource="table1.selectedRows"
style="margin: 52px 0 8px"
@selectRowChange="handleTable3SelectRowChange"
/>
<!-- 右下子 -->
<JVxeTable
toolbar
rowNumber
rowSelection
clickSelectRow
:height="350"
:loading="table4.loading"
:columns="table4.columns"
:dataSource="table4.dataSource"
:pagination="table4.pagination"
style="margin: 48px 0 0"
/>
</a-col>
</a-row>
</a-card>
</template>
<script>
import { defHttp } from '/@/utils/http/axios';
import { JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
export default {
name: 'Template4',
data() {
return {
table1: {
// 是否正在加载
loading: false,
// 分页器参数
pagination: {
// 当前页码
current: 1,
// 每页的条数
pageSize: 200,
// 可切换的条数
pageSizeOptions: ['10', '20', '30', '100', '200'],
// 数据总数目前并不知道真实的总数所以先填写0在后台查出来后再赋值
total: 0,
},
// 最后选中的行
lastRow: null,
// 选择的行
selectedRows: [],
// 数据源,控制表格的数据
dataSource: [],
// 列配置,控制表格显示的列
columns: [
{ key: 'num', title: '序号', width: '80px' },
{
// 字段key跟后台数据的字段名匹配
key: 'ship_name',
// 列的标题
title: '船名',
// 列的宽度
width: '180px',
// 如果加上了该属性就代表当前单元格是可编辑的type就是表单的类型input就是简单的输入框
type: JVxeTypes.input,
},
{ key: 'call', title: '呼叫', width: '80px', type: JVxeTypes.input },
{ key: 'len', title: '长', width: '80px', type: JVxeTypes.input },
{ key: 'ton', title: '吨', width: '120px', type: JVxeTypes.input },
{ key: 'payer', title: '付款方', width: '120px', type: JVxeTypes.input },
{ key: 'count', title: '数', width: '40px' },
{
key: 'company',
title: '公司',
// 最小宽度,与宽度不同的是,这个不是固定的宽度,如果表格有多余的空间,会平均分配给设置了 minWidth 的列
// 如果要做占满表格的列可以这么写
minWidth: '180px',
type: JVxeTypes.input,
},
{ key: 'trend', title: '动向', width: '120px', type: JVxeTypes.input },
],
},
// 子级表的配置信息 (配置和主表的完全一致,就不写冗余的注释了)
table2: {
loading: false,
pagination: { current: 1, pageSize: 200, pageSizeOptions: ['100', '200'], total: 0 },
selectedRows: [],
dataSource: [],
columns: [
{ key: 'dd_num', title: '调度序号', width: '120px' },
{ key: 'tug', title: '拖轮', width: '180px', type: JVxeTypes.input },
{ key: 'work_start_time', title: '作业开始时间', width: '180px', type: JVxeTypes.input },
{ key: 'work_stop_time', title: '作业结束时间', width: '180px', type: JVxeTypes.input },
{ key: 'type', title: '船舶分类', width: '120px', type: JVxeTypes.input },
{ key: 'port_area', title: '所属港区', width: '120px', type: JVxeTypes.input },
],
},
table3: {
// 最后选中的行
lastRow: null,
// 选择的行
selectedRows: [],
},
table4: {
loading: false,
pagination: { current: 1, pageSize: 200, pageSizeOptions: ['100', '200'], total: 0 },
selectedRows: [],
dataSource: [],
columns: [
{ key: 'dd_num', title: '调度序号', width: '120px' },
{ key: 'tug', title: '拖轮', width: '180px', type: JVxeTypes.input },
{ key: 'work_start_time', title: '作业开始时间', width: '180px', type: JVxeTypes.input },
{ key: 'work_stop_time', title: '作业结束时间', width: '180px', type: JVxeTypes.input },
{ key: 'type', title: '船舶分类', width: '120px', type: JVxeTypes.input },
{ key: 'port_area', title: '所属港区', width: '120px', type: JVxeTypes.input },
],
},
// 查询url地址
url: {
getData: '/mock/vxe/getData',
},
};
},
// 监听器
watch: {
// 监听table1 左上【主表】选择的数据发生了变化
['table1.lastRow']() {
this.loadTable2Data();
},
// 监听table3 右上【主表】选择的数据发生了变化
['table3.lastRow']() {
this.loadTable4Data();
},
},
created() {
this.loadTable1Data();
},
methods: {
// 加载table1左上【主表】的数据
loadTable1Data() {
// 封装查询条件
let formData = {
pageNo: this.table1.pagination.current,
pageSize: this.table1.pagination.pageSize,
};
// 调用查询数据接口
this.table1.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
// 后台查询回来的 total数据总数量
this.table1.pagination.total = result.total;
// 将查询的数据赋值给 dataSource
this.table1.dataSource = result.records;
// 重置选择
this.table1.selectedRows = [];
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
this.table1.loading = false;
});
},
// 当table1左上【主表】分页参数变化时触发的事件
handleTable1PageChange(event) {
// 重新赋值
this.table1.pagination.current = event.current;
this.table1.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable1Data();
},
// table1左上【主表】当选择的行变化时触发的事件
handleTable1SelectRowChange(event) {
this.handleTableSelectRowChange(this.table1, event);
},
// 加载table2左下【子表】的数据根据主表的id进行查询
loadTable2Data() {
// 如果主表没有选择,则不查询
let selectedRows = this.table1.selectedRows;
if (!selectedRows || selectedRows.length === 0) {
this.table2.pagination.total = 0;
this.table2.dataSource = [];
this.table2.selectedRows = [];
return;
} else if (this.table1.lastRow == null) {
this.table1.lastRow = selectedRows[selectedRows.length - 1];
}
let formData = {
parentId: this.table1.lastRow.id,
pageNo: this.table2.pagination.current,
pageSize: this.table2.pagination.pageSize,
};
this.table2.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
this.table2.pagination.total = result.total;
this.table2.dataSource = result.records;
this.table2.selectedRows = [];
})
.finally(() => {
this.table2.loading = false;
});
},
// 当table2左下【子表】分页参数变化时触发的事件
handleTable2PageChange(event) {
// 重新赋值
this.table2.pagination.current = event.current;
this.table2.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable2Data();
},
// table3右上【主表】当选择的行变化时触发的事件
handleTable3SelectRowChange(event) {
this.handleTableSelectRowChange(this.table3, event);
},
// 加载table4右下【子表】的数据根据主表的id进行查询
loadTable4Data() {
let parentIds = [];
// 如果主表没有选择,则不查询
let selectedRows = this.table3.selectedRows;
if (!selectedRows || selectedRows.length === 0) {
this.table4.pagination.total = 0;
this.table4.dataSource = [];
this.table4.selectedRows = [];
return;
} else if (this.table3.lastRow == null) {
this.table3.lastRow = selectedRows[selectedRows.length - 1];
}
let formData = {
parentId: this.table3.lastRow.id,
pageNo: this.table4.pagination.current,
pageSize: this.table4.pagination.pageSize,
};
this.table4.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
this.table4.pagination.total = result.total;
this.table4.dataSource = result.records;
this.table4.selectedRows = [];
})
.finally(() => {
this.table4.loading = false;
});
},
// 当table4右下【子表】分页参数变化时触发的事件
handleTable4PageChange(event) {
// 重新赋值
this.table4.pagination.current = event.current;
this.table4.pagination.pageSize = event.pageSize;
// 查询数据
this.loadTable4Data();
},
/** 公共方法:处理表格选中变化事件 */
handleTableSelectRowChange(table, event) {
let { row, action, selectedRows, $table } = event;
// 获取最后一个选中的
let lastSelected = selectedRows[selectedRows.length - 1];
if (action === 'selected') {
table.lastRow = row;
} else if (action === 'selected-all') {
// 取消全选
if (selectedRows.length === 0) {
table.lastRow = null;
} else if (!table.lastRow) {
table.lastRow = lastSelected;
}
} else if (action === 'unselected' && row === table.lastRow) {
table.lastRow = lastSelected;
}
$table.setCurrentRow(table.lastRow);
table.selectedRows = selectedRows;
},
},
};
</script>
<style scoped></style>

View File

@ -0,0 +1,221 @@
<template>
<a-card :bordered="false">
<a-row :gutter="8">
<a-col :span="6">
<!-- 加上 show-line 属性后展开收起图标自动变成 +- 样式 -->
<a-tree
class="template-5-tree"
:tree-data="treeData"
show-icon
show-line
:expandedKeys="treeExpandedKeys"
:selectedKeys="[pagination.current]"
@expand="handleTreeExpand"
@select="handleTreeSelect"
>
<!-- 自定义子节点图标 -->
<a-icon slot="myIcon" type="unordered-list" style="color: #0c8fcf" />
</a-tree>
</a-col>
<a-col :span="18">
<JVxeTable
rowNumber
rowSelection
:height="750"
:loading="loading"
:columns="columns"
:dataSource="dataSource"
:pagination="pagination"
@pageChange="handleTablePageChange"
/>
</a-col>
</a-row>
</a-card>
</template>
<script>
import { defHttp } from '/@/utils/http/axios';
import { JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
// 【多种布局模板】左侧为树,右侧为行编辑
export default {
name: 'Template5',
data() {
return {
// 是否正在加载
loading: false,
// 分页器参数
pagination: {
// 当前页码
current: 1,
// 每页的条数
pageSize: 50,
// 可切换的条数
pageSizeOptions: ['50'],
// 数据总数目前并不知道真实的总数所以先填写0在后台查出来后再赋值
total: 0,
},
// 选择的行
selectedRows: [],
// 数据源,控制表格的数据
dataSource: [],
// 列配置,控制表格显示的列
columns: [
{ key: 'num', title: '序号', width: '80px' },
{
// 字段key跟后台数据的字段名匹配
key: 'ship_name',
// 列的标题
title: '船名',
// 列的宽度
width: '180px',
// 如果加上了该属性就代表当前单元格是可编辑的type就是表单的类型input就是简单的输入框
type: JVxeTypes.input,
},
{ key: 'call', title: '呼叫', width: '80px', type: JVxeTypes.input },
{ key: 'len', title: '长', width: '80px', type: JVxeTypes.input },
{ key: 'ton', title: '吨', width: '120px', type: JVxeTypes.input },
{ key: 'payer', title: '付款方', width: '120px', type: JVxeTypes.input },
{ key: 'count', title: '数', width: '40px' },
{
key: 'company',
title: '公司',
// 最小宽度,与宽度不同的是,这个不是固定的宽度,如果表格有多余的空间,会平均分配给设置了 minWidth 的列
// 如果要做占满表格的列可以这么写
minWidth: '180px',
type: JVxeTypes.input,
},
{ key: 'trend', title: '动向', width: '120px', type: JVxeTypes.input },
],
// 树的数据,这里模拟分页固定数据,实际情况应该是后台查出来的数据
treeData: [
// 第1级数据
{
title: '1-10页',
key: '1-10',
// 第2级数据
children: [
{ title: '第 1 页', key: 1, slots: { icon: 'myIcon' } },
{ title: '第 2 页', key: 2, slots: { icon: 'myIcon' } },
{
title: '第 3 页',
key: 3,
slots: { icon: 'myIcon' },
// 第3级数据
children: [
{ title: '第 333 页', key: 333, slots: { icon: 'myIcon' } },
{ title: '第 444 页', key: 444, slots: { icon: 'myIcon' } },
{ title: '第 555 页', key: 555, slots: { icon: 'myIcon' } },
// 第4第5级以此类推加上 children 属性即可
],
},
{ title: '第 4 页', key: 4, slots: { icon: 'myIcon' } },
{ title: '第 5 页', key: 5, slots: { icon: 'myIcon' } },
{ title: '第 6 页', key: 6, slots: { icon: 'myIcon' } },
{ title: '第 7 页', key: 7, slots: { icon: 'myIcon' } },
{ title: '第 8 页', key: 8, slots: { icon: 'myIcon' } },
{ title: '第 9 页', key: 9, slots: { icon: 'myIcon' } },
{ title: '第 10 页', key: 10, slots: { icon: 'myIcon' } },
],
slots: { icon: 'myIcon' },
},
{
title: '11-20页',
key: '11-20',
children: [
{ title: '第 11 页', key: 11, slots: { icon: 'myIcon' } },
{ title: '第 12 页', key: 12, slots: { icon: 'myIcon' } },
{ title: '第 13 页', key: 13, slots: { icon: 'myIcon' } },
{ title: '第 14 页', key: 14, slots: { icon: 'myIcon' } },
{ title: '第 15 页', key: 15, slots: { icon: 'myIcon' } },
{ title: '第 16 页', key: 16, slots: { icon: 'myIcon' } },
{ title: '第 17 页', key: 17, slots: { icon: 'myIcon' } },
{ title: '第 18 页', key: 18, slots: { icon: 'myIcon' } },
{ title: '第 19 页', key: 19, slots: { icon: 'myIcon' } },
{ title: '第 20 页', key: 20, slots: { icon: 'myIcon' } },
],
slots: { icon: 'myIcon' },
},
],
// 树展开的列,默认 1-10
treeExpandedKeys: ['1-10'],
// 查询url地址
url: {
getData: '/mock/vxe/getData',
},
};
},
created() {
this.loadData();
},
methods: {
// 加载行编辑的数据
loadData() {
// 封装查询条件
let formData = {
pageNo: this.pagination.current,
pageSize: this.pagination.pageSize,
};
// 调用查询数据接口
this.loading = true;
defHttp
.get({
url: this.url.getData,
params: formData,
})
.then((result) => {
// 后台查询回来的 total数据总数量
this.pagination.total = result.total;
// 将查询的数据赋值给 dataSource
this.dataSource = result.records;
// 重置选择
this.selectedRows = [];
})
.finally(() => {
// 这里是无论成功或失败都会执行的方法在这里关闭loading
this.loading = false;
});
},
handleTablePageChange(event) {
// 重新赋值
this.pagination.current = event.current;
this.pagination.pageSize = event.pageSize;
// 查询数据
this.loadData();
// 判断树展开的key
if (event.current <= 10) {
this.treeExpandedKeys = ['1-10'];
} else {
this.treeExpandedKeys = ['11-20'];
}
},
// 树被选择触发的事件
handleTreeSelect(selectedKeys) {
let key = selectedKeys[0];
if (typeof key === 'string') {
// 控制树展开为当前选择的列
this.treeExpandedKeys = selectedKeys;
} else {
this.pagination.current = key;
this.loadData();
}
},
// 树被选择触发的事件
handleTreeExpand(expandedKeys) {
this.treeExpandedKeys = expandedKeys;
},
},
};
</script>
<style lang="less">
/** 隐藏文件小图标 */
.template-5-tree.ant-tree {
li span.ant-tree-switcher.ant-tree-switcher-noop {
display: none;
}
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<a-card :bordered="false">
<a-tabs>
<a-tab-pane tab="ERP布局模板" key="erp">
<erp-template />
</a-tab-pane>
<a-tab-pane tab="布局模板1" key="1">
<template1 />
</a-tab-pane>
<a-tab-pane tab="布局模板2" key="2">
<template2 />
</a-tab-pane>
<a-tab-pane tab="布局模板3" key="3">
<template3 />
</a-tab-pane>
<a-tab-pane tab="布局模板4" key="4">
<template4 />
</a-tab-pane>
<a-tab-pane tab="布局模板5" key="5">
<template5 />
</a-tab-pane>
</a-tabs>
</a-card>
</template>
<script lang="ts" setup>
import Template1 from './Template1.vue';
import Template2 from './Template2.vue';
import Template3 from './Template3.vue';
import Template4 from './Template4.vue';
import Template5 from './Template5.vue';
import ErpTemplate from './ErpTemplate.vue';
</script>
<style scoped></style>

View File

@ -0,0 +1,157 @@
<template>
<BasicForm
ref="formElRef"
:class="'jee-select-demo-form'"
:labelCol="{ span: 6 }"
:wrapperCol="{ span: 14 }"
:showResetButton="false"
:showSubmitButton="false"
:schemas="schemas"
:actionColOptions="{ span: 24 }"
@submit="handleSubmit"
@reset="handleReset"
style="height: 100%"
>
<template #jAreaLinkage="{ model, field }">
<JAreaLinkage v-model:value="model[field]" :showArea="true" :showAll="false" />
</template>
<template #jAreaLinkage1="{ model, field }">
<JAreaLinkage :disabled="isDisabledAuth(['demo.dbarray'])" v-model:value="model[field]" :showArea="true" :showAll="false" />
</template>
<template #JPopup="{ model, field }">
<JPopup v-model:value="model[field]" :formElRef="formElRef" code="report_user" :fieldConfig="[{ source: 'username', target: 'pop1' }]" />
</template>
<template #JAreaSelect="{ model, field }">
<JAreaSelect v-model:value="model[field]" />
</template>
<template #JCheckbox="{ model, field }">
<JCheckbox v-model:value="model[field]" dictCode="remindMode" />
</template>
<template #JInput="{ model, field }">
<JInput v-model:value="model[field]" :type="model['jinputtype']" />
</template>
<template #dargVerify="{ model, field }">
<BasicDragVerify v-model:value="model[field]" />
</template>
<template #superQuery="{ model, field }">
<super-query :config="superQueryConfig" @search="(value)=>handleSuperQuery(value, model, field)"/>
</template>
<template #superQuery1="{ model, field }">
<super-query :config="superQueryConfig" @search="(value)=>handleSuperQuery(value, model, field)" :isCustomSave="true" :saveSearchData="saveSearchData" :save="handleSuperQuerySave"/>
</template>
</BasicForm>
</template>
<script lang="ts">
import { computed, defineComponent, unref, ref } from 'vue';
import { BasicForm, ApiSelect, JAreaLinkage, JPopup, JAreaSelect, FormActionType, JCheckbox, JInput, JEllipsis } from '/@/components/Form';
import { useMessage } from '/@/hooks/web/useMessage';
import { optionsListApi } from '/@/api/demo/select';
import { useDebounceFn } from '@vueuse/core';
import { schemas } from './jeecgComponents.data';
import { usePermission } from '/@/hooks/web/usePermission';
import { BasicDragVerify } from '/@/components/Verify';
export default defineComponent({
components: {
BasicForm,
ApiSelect,
JAreaLinkage,
JPopup,
JAreaSelect,
JCheckbox,
JInput,
JEllipsis,
BasicDragVerify,
},
name: 'JeecgComponents',
setup() {
const { isDisabledAuth } = usePermission();
const check = ref(null);
const formElRef = ref<Nullable<FormActionType>>(null);
const { createMessage } = useMessage();
const keyword = ref<string>('');
const submitButtonOptions = ref({
text: '确定',
});
const searchParams = computed<Recordable>(() => {
return { keyword: unref(keyword) };
});
function onSearch(value: string) {
keyword.value = value;
}
const superQueryConfig = {
name:{ title: "名称", view: "text", type: "string", order: 1 },
birthday:{ title: "生日", view: "date", type: "string", order: 2 },
age:{ title: "年龄", view: "number", type: "number", order: 4 },
sex:{ title: "性别", view: "list", type: "string", dictCode: "sex", order: 5 },
bpmStatus:{ title: "流程状态", view: "list_multi", type: "string", dictCode: "bpm_status", order: 6 },
}
function handleSuperQuery(value, model, field){
if(value){
let str = decodeURI(value.superQueryParams)
console.log(str)
model[field] = str
}
}
const saveSearchData = ref([
{
content: '[{"field":"age","rule":"eq","val":14}]',
title: '豆蔻年华',
type: 'and',
},
{
content: '[{"field":"name","rule":"eq","val":"张三"}]',
title: '项目经理',
type: 'and',
},
]);
const handleSuperQuerySave = (data) => {
// 高级查询保存后的信息
return new Promise<void>((resolve, reject) => {
// 模拟接口
setTimeout(() => {
if (Math.random() > 0.5) {
console.log('接口成功~');
saveSearchData.value = data;
resolve();
} else {
console.log('接口失败~');
reject();
}
}, 1e3);
});
}
return {
schemas,
formElRef,
isDisabledAuth,
optionsListApi,
submitButtonOptions,
onSearch: useDebounceFn(onSearch, 300),
searchParams,
superQueryConfig,
handleSuperQuery,
handleReset: () => {
keyword.value = '';
},
handleSubmit: (values: any) => {
console.log('values:', values);
createMessage.success('click search,values:' + JSON.stringify(values));
},
check,
handleSuperQuerySave,
saveSearchData,
};
},
});
</script>
<style lang="less" scoped>
/**update-begin-author:taoyan date:20220324 for: VUEN-351【vue3】展示不全*/
.jee-select-demo-form .ant-col-5 {
flex: 0 0 159px;
max-width: 159px;
}
/**update-end-author:taoyan date:20220324 for: VUEN-351【vue3】展示不全*/
</style>

View File

@ -0,0 +1,93 @@
<template>
<a-card :bordered="false">
<a-row>
<!-- 左侧文件树 -->
<a-col :span="4" class="clName">
<a-tree :treeData="treeData" :defaultExpandAll="true" @select="onSelect" style="height: 500px; overflow-y: auto"> </a-tree>
</a-col>
<!--右侧缩略图-->
<a-col :span="18">
<div v-for="(file, key) in dataSource" :key="key">
<a-col :span="24">
<a-divider orientation="left">{{ file.fileName }}</a-divider>
</a-col>
<!-- 预览区域 -->
<a-col :span="24">
<template v-if="file.filePdfPath">
<div @click="pdfPreview(file.title)">
<img style="width: 80px; height: 80px" src="../../../assets/images/pdf4.jpg" />
</div>
</template>
<template v-else> (暂无材料,点击"选择文件""扫描上传"上传文件) </template>
</a-col>
</div>
</a-col>
</a-row>
<div style="display: none">
<iframe id="pdfPreviewIframe" :src="url" frameborder="0" width="100%" height="550px" scrolling="auto"></iframe>
</div>
</a-card>
</template>
<script lang="ts">
import { defineComponent, ref, unref, onMounted } from 'vue';
import { useGlobSetting } from '/@/hooks/setting';
import { getToken } from '/@/utils/auth';
const mockdata = [
{
id: '1',
key: '1',
title: '实例.pdf',
fileCode: 'shili',
fileName: '实例',
filePdfPath: '实例',
},
];
export default defineComponent({
name: 'JeecgPdfView',
setup() {
const glob = useGlobSetting();
const treeData = ref([
{
title: '所有PDF电子档',
key: '0-0',
children: mockdata,
},
]);
const dataSource = ref(mockdata);
const allData = ref(mockdata);
const url = ref(`${glob.domainUrl}/sys/common/pdf/pdfPreviewIframe`);
/**
* 打开iframe窗口
* @param title
*/
function pdfPreview(title) {
let iframe = document.getElementById('pdfPreviewIframe');
let json = { title: title, token: getToken() };
iframe.contentWindow.postMessage(json, '*');
}
// 选择PDF文件
function onSelect(selectedKeys, info) {
dataSource.value = [];
if (selectedKeys[0] === undefined || selectedKeys[0] === '0-0') {
dataSource.value = unref(allData);
} else {
dataSource.value.push(info.node.dataRef);
}
}
return {
url,
dataSource,
treeData,
allData,
onSelect,
pdfPreview,
};
},
});
</script>

View File

@ -0,0 +1,102 @@
/** [表格主题样式一] 表格强制列不换行 */
.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;
}
}
/** 查询区域通用样式*/
.table-page-search-wrapper {
.ant-form-inline {
.ant-form-item {
display: flex;
margin-bottom: 24px;
margin-right: 0;
.ant-form-item-control-wrapper {
flex: 1 1;
display: inline-block;
vertical-align: middle;
}
> .ant-form-item-label {
line-height: 32px;
padding-right: 8px;
width: auto;
}
.ant-form-item-control {
height: 32px;
line-height: 32px;
}
}
}
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
}
/*列表上方操作按钮区域*/
.ant-card-body .table-operator {
margin-bottom: 8px;
}
/** Button按钮间距 */
.table-operator .ant-btn {
margin: 0 8px 8px 0;
}
.table-operator .ant-btn-group .ant-btn {
margin: 0;
}
.table-operator .ant-btn-group .ant-btn:last-child {
margin: 0 8px 8px 0;
}
/*列表td的padding设置 可以控制列表大小*/
.ant-table-tbody .ant-table-row td {
padding-top: 15px;
padding-bottom: 15px;
}
/*列表页面弹出modal*/
.ant-modal-cust-warp {
height: 100%;
}
/*弹出modal Y轴滚动条*/
.ant-modal-cust-warp .ant-modal-body {
height: calc(100% - 110px) !important;
overflow-y: auto;
}
/*弹出modal 先有content后有body 故滚动条控制在body上*/
.ant-modal-cust-warp .ant-modal-content {
height: 90% !important;
overflow-y: hidden;
}
/*列表中有图片的加这个样式 参考用户管理*/
.anty-img-wrap {
height: 25px;
position: relative;
}
.antd-more a {
color: #000000;
}

View File

@ -0,0 +1,419 @@
<template>
<a-card :bordered="false">
<!-- 操作按钮区域 -->
<div class="table-operator">
<a-button @click="handleAdd" type="primary" preIcon="ant-design:plus">新增</a-button>
<!-- <a-button type="primary" preIcon="ant-design:download" @click="handleExportExcel('单表原生列表')">导出</a-button>-->
<!-- <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="handleImportExcel">导入</j-upload-button>-->
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchDel">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</div>
<!-- table区域-begin -->
<div>
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px">
<i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a style="font-weight: 600">{{ selectedRowKeys.length }}</a
>项
<a style="margin-left: 24px" @click="onClearSelected">清空</a>
</div>
<a-table
ref="table"
size="middle"
:scroll="{ x: true }"
bordered
rowKey="id"
class="j-table-force-nowrap"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
@change="handleTableChange"
>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex==='tupian'">
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
<img v-else :src="getImgView(text)" :preview="record.id" alt="" class="anty-img-wrap" />
</template>
<template v-else-if="column.dataIndex==='wenjian'">
<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="downloadFile(text)"> 下载 </a-button>
</template>
<template v-else-if="column.dataIndex==='action'">
<a @click="handleEdit(record)">编辑</a>
<a-divider type="vertical" />
<a-dropdown>
<!-- update-begin--author:liaozhiyang---date:20230803---for【QQYUN-5838】图标改小保持一致 -->
<a class="ant-dropdown-link">更多 <Icon icon="mdi-light:chevron-down"></Icon></a>
<!-- update-end--author:liaozhiyang---date:20230803---for【QQYUN-5838】图标改小保持一致 -->
<template #overlay>
<a-menu class="antd-more">
<a-menu-item>
<a @click="handleDetail(record)">详情</a>
</a-menu-item>
<a-menu-item>
<Popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
<a>删除</a>
</Popconfirm>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
<!-- <template v-else-if="column.dataIndex==='htmlSlot'">
<div v-html="text"></div>
</template>
<template v-else-if="column.dataIndex==='pcaSlot'">
<div>{{ getAreaTextByCode(text) }}</div>
</template> -->
</template>
</a-table>
</div>
<OneNativeModal ref="oneProtogenesisModal" @ok="handleSuccess"></OneNativeModal>
</a-card>
</template>
<script lang="ts" setup>
import '../less/TableExpand.less';
import { onMounted, ref, reactive } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { filterMultiDictText } from '/@/utils/dict/JDictSelectUtil.js';
import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
import OneNativeModal from './components/OneNativeModal.vue';
import { Modal, Popconfirm } from 'ant-design-vue';
import { JSelectUserByDept, JDictSelectTag, JSelectDept, JSearchSelect } from '/@/components/Form';
import Icon from '/@/components/Icon/index';
import { filterObj, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { loadCategoryData } from '/@/api/common/api';
import { getToken } from '/@/utils/auth';
import { useMethods } from '/@/hooks/system/useMethods';
import { downloadFile } from '/@/utils/common/renderUtils';
import { initDictOptions } from '/@/utils/dict';
const { handleExportXls, handleImportXls } = useMethods();
const modalVisible = ref<boolean>(false);
const queryParam = ref<any>({});
const loading = ref<boolean>(false);
const dictOptions = ref<any>([]);
const oneProtogenesisModal = ref();
const tokenHeader = { 'X-Access-Token': getToken() };
//表头
const columns = ref<any>([
{
title: '文本',
align: 'center',
dataIndex: 'name',
},
{
title: '字典下拉',
align: 'center',
dataIndex: 'xiala',
customRender: ({ text }) => (text ? filterMultiDictText(dictOptions.value['xiala'], text) : ''),
},
{
title: '字典单选',
align: 'center',
dataIndex: 'danxuan',
customRender: ({ text }) => (text ? filterMultiDictText(dictOptions.value['danxuan'], text) : ''),
},
{
title: '字典多选',
align: 'center',
dataIndex: 'duoxuan',
customRender: ({ text }) => (text ? filterMultiDictText(dictOptions.value['duoxuan'], text) : ''),
},
{
title: '开关',
align: 'center',
dataIndex: 'kaiguan',
customRender: ({ text }) => (text ? filterMultiDictText(dictOptions.value['kaiguan'], text) : ''),
},
{
title: '日期',
align: 'center',
dataIndex: 'riqi',
customRender: function ({ text }) {
return !text ? '' : text.length > 10 ? text.substr(0, 10) : text;
},
},
{
title: '年月日时分秒',
align: 'center',
dataIndex: 'nyrsfm',
},
{
title: '时间',
align: 'center',
dataIndex: 'shijian',
},
{
title: '文件',
align: 'center',
dataIndex: 'wenjian',
},
{
title: '图片',
align: 'center',
dataIndex: 'tupian',
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
fixed: 'right',
width: 147,
},
]);
const Api = reactive<any>({
list: '/test/jeecgDemo/oneNative/list',
delete: '/test/jeecgDemo/oneNative/delete',
exportXls: '/test/jeecgDemo/oneNative/exportXls',
importExcel: 'test/jeecgDemo/oneNative/importExcel',
});
const dataSource = ref<any>([]);
const toggleSearchStatus = ref<boolean>(false);
const ipagination = ref<any>({
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条';
},
showQuickJumper: true,
showSizeChanger: true,
total: 0,
});
const selectedRowKeys = ref<any>([]);
const selectionRows = ref<any>([]);
const iSorter = ref<any>({ column: 'createTime', order: 'desc' });
const iFilters = ref<any>({});
const { createMessage } = useMessage();
/**
* 复选框选中事件
* @param rowKeys
* @param rows
*/
function onSelectChange(rowKeys, rows) {
selectedRowKeys.value = rowKeys;
selectionRows.value = rows;
}
/**
* 表格改变事件
*/
function handleTableChange({ pagination, filters, sorter }) {
ipagination.value = pagination;
iSorter.value = sorter;
iFilters.value = { ...filters };
}
/**
* 新增
*/
function handleAdd() {
oneProtogenesisModal.value.disableSubmit = false;
oneProtogenesisModal.value.add();
}
/**
* 清除选中行
*/
function onClearSelected() {
selectedRowKeys.value = [];
selectionRows.value = [];
}
/**
* 批量删除
*/
function batchDel() {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
defHttp.delete({ url: Api.delete, data: { ids: selectedRowKeys.value } }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
}
/**
* 导出excel
*/
function handleExportExcel(title) {
let paramsForm = getQueryParams();
if (selectedRowKeys.value && selectedRowKeys.value.length > 0) {
paramsForm['selections'] = selectedRowKeys.join(',');
}
handleExportXls(title, Api.exportXls, filterObj(paramsForm));
}
/**
* 导入excel
*/
function handleImportExcel(file) {
handleImportXls(file, Api.importExcel, '').then(() => {
handleSuccess();
});
}
/**
* 获取查询参数
*/
function getQueryParams() {
let params = Object.assign(queryParam.value, iSorter.value, iFilters.value);
params.field = getQueryField();
params.pageNo = ipagination.value.current;
params.pageSize = ipagination.value.pageSize;
return filterObj(params);
}
/**
* 字段权限控制
*/
function getQueryField() {
let str = 'id,';
columns.value.forEach(function (value) {
str += ',' + value.dataIndex;
});
return str;
}
/**
* 初始化数据
*/
function loadData(arg?) {
if (arg === 1) {
ipagination.value.current = 1;
}
loading.value = true;
let params = getQueryParams();
defHttp
.get({ url: Api.list, params }, { isTransformResponse: false })
.then((res) => {
if (res.success) {
dataSource.value = res.result.records;
if (res.result && res.result.total) {
ipagination.value.total = res.result.total;
} else {
ipagination.value.total = 0;
}
} else {
createMessage.warning(res.message);
}
})
.finally(() => {
loading.value = false;
});
}
//查询
function searchQuery() {
loadData(1);
selectedRowKeys.value = [];
selectionRows.value = [];
}
/**
* 查询区域展开关闭
*/
function handleToggleSearch() {
toggleSearchStatus.value = !toggleSearchStatus.value;
}
/**
* 重置按钮
*/
function searchReset() {
queryParam.value = {};
loadData(1);
}
/**
* 获取预览图片
*/
function getImgView(text) {
if (text && text.indexOf(',') > 0) {
text = text.substring(0, text.indexOf(','));
}
return getFileAccessHttpUrl(text);
}
/**
* 编辑
* @param record
*/
function handleEdit(record) {
oneProtogenesisModal.value.disableSubmit = false;
oneProtogenesisModal.value.edit(record);
}
/**
* 详情
* @param record
*/
function handleDetail(record) {
oneProtogenesisModal.value.disableSubmit = true;
oneProtogenesisModal.value.edit(record);
}
/**
* 删除
* @param id
*/
function handleDelete(id) {
defHttp.delete({ url: Api.delete, data: { ids: id } }, { joinParamsToUrl: true }).then((res) => {
handleSuccess();
});
}
/**
* 初始化字典选项
*/
async function initDictConfig() {
dictOptions.value['flzds'] = await loadCategoryData({ code: 'B01' });
dictOptions.value['xiala'] = await initDictOptions('sex');
dictOptions.value['danxuan'] = await initDictOptions('sex');
dictOptions.value['duoxuan'] = await initDictOptions('urgent_level');
}
/**
* 保存表单后回调事件
*/
function handleSuccess() {
selectedRowKeys.value = [];
selectionRows.value = [];
loadData(1);
}
onMounted(() => {
dictOptions.value['kaiguan'] = [
{ text: '是', value: '1' },
{ text: '否', value: '2' },
];
//初始加载页面
loadData();
//初始化字典选项
initDictConfig();
});
</script>

View File

@ -0,0 +1,455 @@
<template>
<a-spin :spinning="confirmLoading">
<a-form class="antd-modal-form" ref="formRef" :model="formState" :rules="validatorRules">
<a-row>
<a-col :span="24">
<a-form-item label="文本" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.name">
<a-input v-model:value="formState.name" placeholder="请输入文本"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="密码" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.miMa">
<a-input-password v-model:value="formState.miMa" placeholder="请输入密码" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典下拉" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.xiala">
<JDictSelectTag type="select" v-model:value="formState.xiala" dictCode="sex" placeholder="请选择字典下拉" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典单选" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.danxuan">
<JDictSelectTag type="radio" v-model:value="formState.danxuan" dictCode="sex" placeholder="请选择字典单选" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典多选" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.duoxuan">
<JCheckbox v-model:value="formState.duoxuan" dictCode="urgent_level" placeholder="请选择字典多选" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="开关" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.kaiguan">
<JSwitch v-model:value="formState.kaiguan" :options="['1', '0']"></JSwitch>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="日期" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.riqi">
<a-date-picker placeholder="请选择日期" format="YYYY-MM-DD" valueFormat="YYYY-MM-DD" v-model:value="formState.riqi" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="年月日时分秒" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.nyrsfm">
<a-date-picker show-time v-model:value="formState.nyrsfm" style="width: 100%" valueFormat="YYYY-MM-DD HH:mm:ss" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="时间" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.shijian">
<TimePicker placeholder="请选择时间" v-model:value="formState.shijian" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="文件" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.wenjian">
<JUpload v-model:value="formState.wenjian"></JUpload>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="图片" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.tupian">
<JImageUpload :fileMax="2" v-model:value="formState.tupian"></JImageUpload>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="多行文本框" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.dhwb">
<a-textarea v-model:value="formState.dhwb" rows="4" placeholder="请输入多行文本框" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="字典表下拉搜索框" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.xlss">
<JSearchSelect v-model:value="formState.xlss" dict="sys_user,realname,username" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="popup弹窗" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.popup">
<JPopup
v-model:value="formState.popup"
:fieldConfig="[
{ source: 'name', target: 'popup' },
{ source: 'id', target: 'popback' },
]"
code="report_user"
:multi="true"
:setFieldsValue="setFieldsValue"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="popback" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.popback">
<a-input v-model:value="formState.popback" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="分类字典树" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.flzds">
<JCategorySelect
@change="(value) => handleFormChange('flzds', value)"
v-model:value="formState.flzds"
pcode="B02"
placeholder="请选择分类字典树"
/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="部门选择" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.bmxz">
<JSelectDept v-model:value="formState.bmxz" :multi="true" type="array" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="用户选择" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.yhxz">
<JSelectUserByDept v-model:value="formState.yhxz" :multi="true" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="富文本" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.fwb">
<JEditor v-model:value="formState.fwb" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="markdown" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.markdownString">
<JMarkdownEditor v-model:value="formState.markdownString"></JMarkdownEditor>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="省市区JAreaSelect" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.shq">
<JAreaSelect v-model:value="formState.shq" placeholder="请输入省市区" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="省市区JAreaLinkage" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.jssq">
<JAreaLinkage v-model:value="formState.jssq" placeholder="请输入省市区" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JInputPop" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.ldzje">
<JInputPop
v-model:value="formState.ldzje"
placeholder="请输入JInputPop"
@change="(value) => handleFormChange('ldzje', value)"
></JInputPop>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JSelectInput" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.ldzjs">
<JSelectInput
v-model:value="formState.ldzjs"
placeholder="请选择JSelectInput"
:options="ldzjsOptions"
@change="(value) => handleFormChange('ldzjs', value)"
></JSelectInput>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="下拉多选" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zddtjxl">
<JSelectMultiple v-model:value="formState.zddtjxl" placeholder="请选择下拉多选" dictCode="sex"></JSelectMultiple>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="用户" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.yongHu">
<JSelectUser v-model:value="formState.yongHu" placeholder="请选择用户"></JSelectUser>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="职务" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zhiWu">
<JSelectPosition
v-model:value="formState.zhiWu"
placeholder="请选择职务"
@change="(value) => handleFormChange('zhiWu', value)"
></JSelectPosition>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="角色" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.jueSe">
<JSelectRole v-model:value="formState.jueSe" placeholder="请选择角色" @change="(value) => handleFormChange('jueSe', value)"></JSelectRole>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="自定义树" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zdys">
<JTreeSelect
ref="treeSelect"
placeholder="请选择自定义树"
v-model:value="formState.zdys"
dict="sys_category,name,id"
pidValue="0"
loadTriggleChange
>
</JTreeSelect>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="数值" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.yuanjia">
<a-input-number v-model:value="formState.yuanjia" placeholder="请输入double类型" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="输入2到10位的字母" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.ywzz">
<a-input v-model:value="formState.ywzz" placeholder="请输入2到10位的字母"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JTreeDict" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zdbxl">
<JTreeDict
v-model:value="formState.zdbxl"
placeholder="请选择JTreeDict"
@change="(value) => handleFormChange('zdbxl', value)"
></JTreeDict>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="JCodeEditor" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.zdmrz">
<JCodeEditor
v-model:value="formState.zdmrz"
placeholder="请输入JCodeEditor"
@change="(value) => handleFormChange('zdmrz', value)"
></JCodeEditor>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="参数" :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.jsonParam">
<JAddInput v-model:value="formState.jsonParam" placeholder="参数"></JAddInput>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
</template>
<script lang="ts" setup>
import { ref, reactive, nextTick } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import dayjs from 'dayjs';
import { TimePicker, Form } from 'ant-design-vue';
import JCheckbox from '/@/components/Form/src/jeecg/components/JCheckbox.vue';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
import JSwitch from '/@/components/Form/src/jeecg/components/JSwitch.vue';
import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
import JImageUpload from '/@/components/Form/src/jeecg/components/JImageUpload.vue';
import JSearchSelect from '/@/components/Form/src/jeecg/components/JSearchSelect.vue';
import JPopup from '/@/components/Form/src/jeecg/components/JPopup.vue';
import JCategorySelect from '/@/components/Form/src/jeecg/components/JCategorySelect.vue';
import JSelectUserByDept from '/@/components/Form/src/jeecg/components/JSelectUserByDept.vue';
import JEditor from '/@/components/Form/src/jeecg/components/JEditor.vue';
import JMarkdownEditor from '/@/components/Form/src/jeecg/components/JMarkdownEditor.vue';
import JTreeSelect from '/@/components/Form/src/jeecg/components/JTreeSelect.vue';
import JInputPop from '/@/components/Form/src/jeecg/components/JInputPop.vue';
import JSelectInput from '/@/components/Form/src/jeecg/components/JSelectInput.vue';
import JSelectPosition from '/@/components/Form/src/jeecg/components/JSelectPosition.vue';
import JSelectMultiple from '/@/components/Form/src/jeecg/components/JSelectMultiple.vue';
import JInput from '/@/components/Form/src/jeecg/components/JInput.vue';
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
import JSelectUser from '/@/components/Form/src/jeecg/components/JSelectUser.vue';
import JAreaSelect from '/@/components/Form/src/jeecg/components/JAreaSelect.vue';
import JAreaLinkage from '/@/components/Form/src/jeecg/components/JAreaLinkage.vue';
import JSelectRole from '/@/components/Form/src/jeecg/components/JSelectRole.vue';
import JTreeDict from '/@/components/Form/src/jeecg/components/JTreeDict.vue';
import JCodeEditor from '/@/components/Form/src/jeecg/components/JCodeEditor.vue';
import JAddInput from '/@/components/Form/src/jeecg/components/JAddInput.vue';
import { getValueType } from '/@/utils';
const emit = defineEmits(['register', 'ok']);
//update-begin---author:wangshuai ---date:20220616 for报表示例验证修改--------------
const formState = reactive<Record<string, any>>({
name: '',
miMa: '',
ywzz: '',
xiala: '',
danxuan: '',
duoxuan: '',
riqi: '',
shijian: '',
wenjian: '',
tupian: '',
dhwb: '',
xlss: '',
popup: '',
flzds: '',
yhxz: '',
fwb: '',
shq: '',
ldzje: '',
ldzjs: '',
zddtjxl: '',
yongHu: '',
zhiWu: '',
jueSe: '',
zdys: '',
jssq: '',
zdbxl: '',
zdmrz: '',
jsonParam: '',
bmxz: '',
yuanjia: '',
nyrsfm: '',
});
//update-end---author:wangshuai ---date:20220616 for报表示例验证修改--------------
const { createMessage } = useMessage();
const formRef = ref();
const useForm = Form.useForm;
const url = reactive<any>({
duplicateCheck: '/sys/duplicate/check',
add: '/test/jeecgDemo/oneNative/add',
edit: '/test/jeecgDemo/oneNative/edit',
});
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false);
//表单验证
const validatorRules = {
name: [{ required: false, message: '请输入文本!' }],
miMa: [{ required: false, message: '请输入密码!' }],
ywzz: [{ required: false }, { pattern: '^[a-z|A-Z]{2,10}$', message: '不符合校验规则!' }],
xiala: [{ required: false, message: '请选择下拉组件!' }],
danxuan: [{ required: false, message: '请选择单选组件!' }],
duoxuan: [{ required: false, message: '请选择多选组件!' }],
riqi: [{ required: false, message: '请选择日期!' }],
shijian: [{ required: false, message: '请选择时间!' }],
wenjian: [{ required: false, message: '请上传文件!' }],
tupian: [{ required: false, message: '请上传图片!' }],
dhwb: [{ required: false, message: '请填写多行文本!' }],
xlss: [{ required: false, message: '请选择字典下拉搜索!' }],
popup: [{ required: false, message: '请选择popup弹窗!' }],
flzds: [{ required: false, message: '请选择分类字典树!' }],
yhxz: [{ required: false, message: '请选择用户!' }],
fwb: [{ required: false, message: '请填写富文本!' }],
shq: [{ required: false, message: '请选择省市级!' }],
ldzje: [{ required: false, message: '请输入JInputPop!' }],
ldzjs: [{ required: false, message: '请选择下拉输入框!' }],
zddtjxl: [{ required: false, message: '请选择多选输入框!' }],
yongHu: [{ required: false, message: '请选择用户!' }],
zhiWu: [{ required: false, message: '请选择职务!' }],
jueSe: [{ required: false, message: '请选择角色!' }],
zdys: [{ required: false, message: '请选择自定义树!' }],
jssq: [{ required: false, message: '请选择三级联动!' }],
zdbxl: [{ required: false, message: '请选择JTreeDict!' }],
zdmrz: [{ required: false, message: '请输入JCodeEditor!' }],
jsonParam: [{ required: false, message: '请输入参数!' }],
bmxz: [{ required: false, message: '请选择部门!' }],
yuanjia: [{ required: false, message: '请输入数值!' }],
nyrsfm: [{ required: false, message: '请选择年月日时分秒!' }],
};
//update-begin---author:wangshuai ---date:20220616 for报表示例验证修改------------
const { resetFields, validate, validateInfos } = useForm(formState, validatorRules, { immediate: false });
//update-end---author:wangshuai ---date:20220616 for报表示例验证修改------------
const ldzjsOptions = ref([
{ label: '男', value: '1' },
{ label: '女', value: '2' },
]);
/**
* 新增
*/
function add() {
edit({});
}
/**
* 编辑
*/
function edit(record) {
nextTick(() => {
resetFields();
//赋值
Object.assign(formState, record);
});
}
/**
* 提交数据
*/
async function submitForm() {
// 触发表单验证
//update-begin---author:wangshuai ---date:20220616 for报表示例验证修改------------
await validate();
confirmLoading.value = true;
let httpurl = '';
let method = '';
//时间格式化
let model = formState;
if (!model.id) {
httpurl += url.add;
method = 'post';
} else {
httpurl += url.edit;
method = 'put';
}
//循环数据如果是数组
for (let data in formState) {
//如果该数据是数组并且是字符串类型
if (formState[data] instanceof Array) {
let valueType = getValueType(formRef.value.getProps, data);
//如果是字符串类型的需要变成以逗号分割的字符串
if (valueType === 'string') {
formState[data] = formState[data].join(',');
}
}
}
defHttp
.request(
{
url: httpurl,
params: model,
method: method,
},
{ isTransformResponse: false }
)
.then((res) => {
if (res.success) {
createMessage.success(res.message);
emit('ok');
} else {
createMessage.warning(res.message);
}
})
.finally(() => {
confirmLoading.value = false;
});
//update-end---author:wangshuai ---date:20220616 for报表示例验证修改--------------
}
/**
* popup成功回调事件
*/
function popupHandleSuccess(values) {
Object.assign(formState, values);
}
/**
* popup组件值改变事件
*/
function setFieldsValue(map) {
Object.keys(map).map((key) => {
formState[key] = map[key];
});
}
/**
* 值改变事件触发
* @param key
* @param value
*/
function handleFormChange(key, value) {
formState[key] = value;
}
defineExpose({
add,
edit,
submitForm,
});
</script>
<style lang="less" scoped>
.antd-modal-form {
padding: 24px 24px 24px 24px;
}
</style>

View File

@ -0,0 +1,65 @@
<template>
<BasicModal
:title="title"
:width="width"
:visible="visible"
:height="600"
@ok="handleOk"
:okButtonProps="{ class: { 'jee-hidden': disableSubmit } }"
@cancel="handleCancel"
cancelText="关闭"
>
<OneNativeForm ref="realForm" @ok="submitCallback" :disabled="disableSubmit"></OneNativeForm>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, nextTick } from 'vue';
import OneNativeForm from './OneNativeForm.vue';
import { BasicModal } from '/@/components/Modal';
const title = ref<string>('');
const width = ref<number>(800);
const visible = ref<boolean>(false);
const disableSubmit = ref<boolean>(false);
const realForm = ref();
const emit = defineEmits(['register', 'ok']);
function add() {
title.value = '新增';
visible.value = true;
nextTick(() => {
realForm.value.add();
});
}
function edit(record) {
title.value = disableSubmit.value ? '详情' : '编辑';
visible.value = true;
nextTick(() => {
realForm.value.edit(record);
});
}
function handleOk() {
realForm.value.submitForm();
}
function submitCallback() {
handleCancel();
emit('ok');
}
function handleCancel() {
visible.value = false;
}
defineExpose({
add,
edit,
disableSubmit,
});
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,202 @@
<template>
<PageWrapper>
<a-card :bordered="false" class="j-print-demo">
<div style="text-align: right">
<a-button type="primary" ghost @click="onPrint">打印</a-button>
</div>
<section ref="print" id="printContent">
<div style="text-align: center">
<p style="font-size: 24px; font-weight: 800">打印测试表单</p>
</div>
<!--签字-->
<a-col :md="24" :sm="24">
<div class="sign" style="text-align: center; height: inherit">
<a-col :span="24">
<span>打印人员:</span>
<a-input style="width: 30%" v-model:value="model.printer" />
<span style="margin-left: 12.5%">打印日期:</span>
<a-input style="width: 30%" v-model:value="model.printTime" />
</a-col>
<a-col :span="24"> </a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印内容:</span>
<a-input style="width: 80%" v-model:value="model.printContent" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的1:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的2:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的3:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的4:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的5:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的6:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的7:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的8:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的9:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的10:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的11:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的12:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的13:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col :span="24" style="margin-top: 20px">
<span>打印目的14:</span>
<a-input style="width: 80%" v-model:value="model.printReason" />
</a-col>
<a-col style="margin-top: 20px" :span="24">
<span>打印图片:</span>
<br />
<a-upload
action="/jsonplaceholder.typicode.com/posts/"
listType="picture-card"
:fileList="model.fileList"
@preview="handlePreview"
@change="handleChange"
>
<div v-if="model.fileList.length < 3">
<Icon icon="ant-design:plus-outlined" />
<div class="ant-upload-text">Upload</div>
</div>
</a-upload>
<a-modal :open="previewVisible" :footer="null" @cancel="previewVisible = false">
<img alt="example" style="width: 100%" :src="previewImage" />
</a-modal>
</a-col>
</div>
</a-col>
</section>
</a-card>
</PageWrapper>
</template>
<script lang="ts">
import { ref, reactive } from 'vue';
import { PageWrapper } from '/@/components/Page';
import Icon from '/@/components/Icon/src/Icon.vue';
import { printJS } from '/@/hooks/web/usePrintJS';
export default {
name: 'PrintDemo',
components: { PageWrapper, Icon },
props: {
reBizCode: {
type: String,
default: '',
},
},
setup() {
const model = reactive({
printer: '张三',
printTime: '2021-12-31 23:59:59',
printContent: '打印内容:这是一个打印测试!',
printReason: '做一个打印测试',
fileList: [
{
uid: '-1',
name: 'xxx.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
},
{
uid: '-2',
name: 'pic1.png',
status: 'done',
url: 'https://www.gizbot.com/img/2016/11/whatsapp-error-lead-image-08-1478607387.jpg',
},
],
});
const previewImage = ref('');
const previewVisible = ref(false);
function onPrint() {
printJS({
printable: '#printContent',
type: 'html',
});
}
function handlePreview(file) {
previewImage.value = file.url || file.thumbUrl;
previewVisible.value = true;
}
function handleChange({ fileList }) {
model.fileList = fileList;
}
return {
model,
previewImage,
previewVisible,
onPrint,
handlePreview,
handleChange,
};
},
};
</script>
<style lang="less" scoped>
.j-print-demo .ant-card-body {
margin-left: 0;
margin-right: 0;
margin-bottom: 1%;
border: 0 solid black;
min-width: 800px;
color: #000000 !important;
}
.sign .ant-input {
font-weight: bolder;
text-align: center;
border-left-width: 0 !important;
border-top-width: 0 !important;
border-right-width: 0 !important;
outline: none !important;
box-shadow: none !important;
}
/* you can make up upload button and sample style by using stylesheets */
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
</style>

View File

@ -0,0 +1,59 @@
<template>
<PageWrapper>
<a-card :bordered="false">
<BasicTable @register="registerTable" />
</a-card>
</PageWrapper>
</template>
<script lang="ts" setup>
import { PageWrapper } from '/@/components/Page';
import { BasicTable, useTable } from '/@/components/Table';
import { mapTableTotalSummary } from '/@/utils/common/compUtils';
const [registerTable] = useTable({
rowKey: 'id',
bordered: true,
canResize: false,
columns: [
{ title: '姓名', dataIndex: 'name' },
{ title: '贡献点', dataIndex: 'point' },
{ title: '等级', dataIndex: 'level' },
{ title: '更新时间', dataIndex: 'updateTime' },
],
dataSource: [
{ id: 0, name: '张三', point: 23, level: 3, updateTime: '2019-8-14' },
{ id: 1, name: '小鹿', point: 33, level: 9, updateTime: '2019-8-10' },
{ id: 2, name: '小王', point: 6, level: 1, updateTime: '2019-8-13' },
{ id: 3, name: '李四', point: 53, level: 8, updateTime: '2019-8-12' },
{ id: 4, name: '小红', point: 44, level: 5, updateTime: '2019-8-11' },
{ id: 5, name: '王五', point: 97, level: 10, updateTime: '2019-8-10' },
{ id: 6, name: '小明', point: 33, level: 2, updateTime: '2019-8-10' },
{ id: 7, name: '小张', point: 33, level: 4, updateTime: '2019-8-10' },
{ id: 8, name: '小六', point: 33, level: 2, updateTime: '2019-8-10' },
{ id: 9, name: '小五', point: 33, level: 7, updateTime: '2019-8-10' },
{ id: 10, name: '小赵', point: 33, level: 2, updateTime: '2019-8-10' },
{ id: 11, name: '李华', point: 33, level: 8, updateTime: '2019-8-10' },
{ id: 12, name: '小康', point: 33, level: 5, updateTime: '2019-8-10' },
],
// 显示底部合计
showSummary: true,
// 底部合计计算方法
summaryFunc: onSummary,
});
function onSummary(tableData: Recordable[]) {
// 可用工具方法自动计算合计
const totals = mapTableTotalSummary(tableData, ['point', 'level']);
return [
totals,
{
_row: '平均',
_index: '平均',
// 计算平均值
point: (totals.point / tableData.length).toFixed(2),
level: (totals.level / tableData.length).toFixed(0),
},
];
}
</script>

View File

@ -0,0 +1,165 @@
<template>
<div>
<!--表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection" :searchInfo="searchInfo">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<!-- 表单区域 -->
<JeecgOrderCustomerModal @register="registerModal" @success="handleSuccess"></JeecgOrderCustomerModal>
</div>
</template>
<script lang="ts" setup>
//ts语法
import type { ComputedRef } from 'vue';
import { ref, computed, unref, watch, inject } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import JeecgOrderCustomerModal from './components/JeecgOrderCustomerModal.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { useModal } from '/@/components/Modal';
import { customColumns } from './erplist.data';
import { customList, deleteCustomer, deleteBatchCustomer } from './erplist.api';
import { isEmpty } from '/@/utils/is';
import { useMessage } from '/@/hooks/web/useMessage';
//接收主表id
const orderId = inject<ComputedRef<string>>(
'orderId',
computed(() => '')
);
//提示弹窗
const $message = useMessage();
//弹窗model
const [registerModal, { openModal }] = useModal();
const searchInfo = {};
// 列表页面公共参数、方法
const { prefixCls, tableContext } = useListPage({
tableProps: {
api: getCustomList,
tableSetting:{
cacheKey:'customer'
},
columns: customColumns,
canResize: false,
useSearchForm: false,
actionColumn: {
width: 180,
},
pagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20'],
},
},
});
//注册table数据
const [registerTable, { reload, setSelectedRowKeys }, { rowSelection, selectedRowKeys }] = tableContext;
watch(orderId, () => {
searchInfo['orderId'] = unref(orderId);
reload();
// 主表id变化时清空子表的选中状态
setSelectedRowKeys([]);
});
async function getCustomList(params) {
let { orderId } = params;
// 主表Id为空时不查询子表数据直接返回空数组
if (orderId == null || isEmpty(orderId)) {
return [];
}
return await customList(params);
}
/**
* 新增事件
*/
function handleCreate() {
console.log('orderId=====', orderId);
if (isEmpty(unref(orderId))) {
$message.createMessage.warning('请选择一个订单信息');
return;
}
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
async function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteCustomer({ id: record.id }, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await deleteBatchCustomer({ ids: selectedRowKeys.value }, () => {
selectedRowKeys.value = [];
reload();
});
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
];
}
</script>
<style scoped></style>

View File

@ -0,0 +1,164 @@
<template>
<div>
<!--表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection" :searchInfo="searchInfo">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<!-- 表单区域 -->
<JeecgOrderTicketModal @register="registerModal" @success="handleSuccess"></JeecgOrderTicketModal>
</div>
</template>
<script lang="ts" setup>
//ts语法
import type { ComputedRef } from 'vue';
import { ref, computed, unref, watch, inject } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import JeecgOrderTicketModal from './components/JeecgOrderTicketModal.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { useModal } from '/@/components/Modal';
import { ticketColumns } from './erplist.data';
import { ticketList, deleteTicket, deleteBatchTicket } from './erplist.api';
import { isEmpty } from '/@/utils/is';
import { useMessage } from '/@/hooks/web/useMessage';
//接收主表id
const orderId = inject<ComputedRef<string>>(
'orderId',
computed(() => '')
);
//提示弹窗
const $message = useMessage();
//弹窗model
const [registerModal, { openModal }] = useModal();
const searchInfo = {};
// 列表页面公共参数、方法
const { prefixCls, tableContext } = useListPage({
tableProps: {
api: getTicketList,
tableSetting:{
cacheKey:'ticket'
},
columns: ticketColumns,
canResize: false,
useSearchForm: false,
actionColumn: {
width: 180,
},
pagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20'],
},
},
});
//注册table数据
const [registerTable, { reload, setSelectedRowKeys }, { rowSelection, selectedRowKeys }] = tableContext;
watch(orderId, () => {
searchInfo['orderId'] = unref(orderId);
reload();
// 主表id变化时清空子表的选中状态
setSelectedRowKeys([]);
});
async function getTicketList(params) {
let { orderId } = params;
// 主表Id为空时不查询子表数据直接返回空数组
if (orderId == null || isEmpty(orderId)) {
return [];
}
return await ticketList(params);
}
/**
* 新增事件
*/
function handleCreate() {
if (isEmpty(unref(orderId))) {
$message.createMessage.warning('请选择一个订单信息');
return;
}
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
async function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteTicket({ id: record.id }, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await deleteBatchTicket({ ids: selectedRowKeys.value }, () => {
selectedRowKeys.value = [];
reload();
});
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
];
}
</script>
<style scoped></style>

View File

@ -0,0 +1,57 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" :width="700">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref, inject } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { customerFormSchema } from '../erplist.data';
import { saveOrUpdateCustomer } from '../erplist.api';
//接收主表id
const orderId = inject('orderId') || '';
// 声明Emits
const emit = defineEmits(['success', 'register']);
const isUpdate = ref(true);
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 150,
schemas: customerFormSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
});
//设置标题
const getTitle = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
const values = await validate();
setModalProps({ confirmLoading: true });
if (unref(orderId)) {
values.orderId = unref(orderId);
}
//提交表单
await saveOrUpdateCustomer(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>

View File

@ -0,0 +1,52 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" :width="700">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from '../erplist.data';
import { saveOrUpdate } from '../erplist.api';
// 声明Emits
const emit = defineEmits(['success', 'register']);
const isUpdate = ref(true);
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
});
//设置标题
const getTitle = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
const values = await validate();
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>

View File

@ -0,0 +1,57 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" :width="500" :minHeight="20" :maxHeight="20">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref, inject } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { ticketFormSchema } from '../erplist.data';
import { saveOrUpdateTicket } from '../erplist.api';
//接收主表id
const orderId = inject('orderId');
// 声明Emits
const emit = defineEmits(['success', 'register']);
const isUpdate = ref(true);
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 150,
schemas: ticketFormSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
});
//设置标题
const getTitle = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
const values = await validate();
setModalProps({ confirmLoading: true });
if (unref(orderId)) {
values.orderId = unref(orderId);
}
//提交表单
await saveOrUpdateTicket(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>

View File

@ -0,0 +1,139 @@
import { defHttp } from '/@/utils/http/axios';
import { Modal } from 'ant-design-vue';
enum Api {
list = '/test/order/orderList',
save = '/test/order/add',
edit = '/test/order/edit',
deleteOne = '/test/order/delete',
deleteBatch = '/test/order/deleteBatch',
customList = '/test/order/listOrderCustomerByMainId',
saveCustomer = '/test/order/addCustomer',
editCustomer = '/test/order/editCustomer',
deleteCustomer = '/test/order/deleteCustomer',
deleteBatchCustomer = '/test/order/deleteBatchCustomer',
ticketList = '/test/order/listOrderTicketByMainId',
saveTicket = '/test/order/addTicket',
editTicket = '/test/order/editTicket',
deleteTicket = '/test/order/deleteTicket',
deleteBatchTicket = '/test/order/deleteBatchTicket',
}
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除
*/
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
* 列表接口
* @param params
*/
export const customList = (params) => defHttp.get({ url: Api.customList, params });
/**
* 删除
*/
export const deleteCustomer = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteCustomer, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const deleteBatchCustomer = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatchCustomer, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdateCustomer = (params, isUpdate) => {
let url = isUpdate ? Api.editCustomer : Api.saveCustomer;
return defHttp.post({ url: url, params });
};
/**
* 列表接口
* @param params
*/
export const ticketList = (params) => defHttp.get({ url: Api.ticketList, params });
/**
* 删除
*/
export const deleteTicket = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteTicket, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
*/
export const deleteBatchTicket = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatchTicket, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdateTicket = (params, isUpdate) => {
let url = isUpdate ? Api.editTicket : Api.saveTicket;
return defHttp.post({ url: url, params });
};

View File

@ -0,0 +1,238 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { render } from '/@/utils/common/renderUtils';
export const columns: BasicColumn[] = [
{
title: '订单号',
dataIndex: 'orderCode',
width: 260,
},
{
title: '订单类型',
dataIndex: 'ctype',
width: 160,
customRender: ({ text }) => {
return text == '1' ? '国内订单' : text == '2' ? '国际订单' : '';
},
},
{
title: '订单日期',
dataIndex: 'orderDate',
width: 300,
},
{
title: '订单金额',
width: 200,
dataIndex: 'orderMoney',
},
{
title: '订单备注',
width: 200,
dataIndex: 'content',
},
];
export const searchFormSchema: FormSchema[] = [
{
label: '订单号',
field: 'orderCode',
component: 'Input',
colProps: { span: 6 },
},
{
label: '订单类型',
field: 'ctype',
component: 'Select',
componentProps: {
options: [
{
label: '国内订单',
value: '1',
key: '1',
},
{
label: '国际订单',
value: '2',
key: '2',
},
],
},
colProps: { span: 6 },
},
];
export const formSchema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '订单号',
field: 'orderCode',
component: 'Input',
required: true,
},
{
label: '订单类型',
field: 'ctype',
component: 'Select',
componentProps: {
options: [
{
label: '国内订单',
value: '1',
key: '1',
},
{
label: '国际订单',
value: '2',
key: '2',
},
],
},
},
{
label: '订单日期',
field: 'orderDate',
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD hh:mm:ss',
},
},
{
label: '订单金额',
field: 'orderMoney',
component: 'InputNumber',
},
{
label: '订单备注',
field: 'content',
component: 'Input',
},
];
export const customColumns: BasicColumn[] = [
{
title: '客户名',
dataIndex: 'name',
width: 260,
},
{
title: '性别',
dataIndex: 'sex',
width: 100,
customRender: ({ text }) => {
return render.renderDict(text, 'sex');
},
},
{
title: '身份证号',
dataIndex: 'idcard',
width: 300,
},
{
title: '电话',
width: 200,
dataIndex: 'telphone',
},
];
export const customerFormSchema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '客户姓名',
field: 'name',
component: 'Input',
required: true,
},
{
label: '性别',
field: 'sex',
component: 'JDictSelectTag',
componentProps: {
dictCode: 'sex',
placeholder: '请选择性别',
},
},
{
label: '身份证号码',
field: 'idcard',
component: 'Input',
},
{
label: '身份证扫描件',
field: 'idcardPic',
component: 'JImageUpload',
componentProps: {
fileMax: 2,
},
},
{
label: '联系方式',
field: 'telphone',
component: 'Input',
rules: [{ required: false, pattern: /^1[3456789]\d{9}$/, message: '手机号码格式有误' }],
},
{
label: 'orderId',
field: 'orderId',
component: 'Input',
show: false,
},
];
export const ticketColumns: BasicColumn[] = [
{
title: '航班号',
dataIndex: 'ticketCode',
},
{
title: '航班时间',
dataIndex: 'tickectDate',
},
{
title: '创建人',
dataIndex: 'createBy',
},
{
title: '创建时间',
dataIndex: 'createTime',
},
];
export const ticketFormSchema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '航班号',
field: 'ticketCode',
component: 'Input',
required: true,
},
{
label: '航班时间',
field: 'tickectDate',
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD',
},
},
{
label: 'orderId',
field: 'orderId',
component: 'Input',
show: false,
},
];

View File

@ -0,0 +1,163 @@
<template>
<div>
<!--主表表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<!--子表表格tab-->
<a-tabs defaultActiveKey="1" style="margin: 10px">
<a-tab-pane tab="客户信息" key="1">
<JeecgOrderCustomerList />
</a-tab-pane>
<a-tab-pane tab="机票信息" key="2" forceRender>
<JeecgOrderTicketList />
</a-tab-pane>
</a-tabs>
</div>
<!-- 表单区域 -->
<JeecgOrderModal @register="registerModal" @success="handleSuccess"></JeecgOrderModal>
</template>
<script lang="ts" name="tab-list" setup>
//ts语法
import { ref, computed, unref, watch, provide } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { useModal } from '/@/components/Modal';
import JeecgOrderModal from './components/JeecgOrderModal.vue';
import JeecgOrderCustomerList from './JeecgOrderCustomerList.vue';
import JeecgOrderTicketList from './JeecgOrderTicketList.vue';
import { columns, searchFormSchema } from './erplist.data';
import { list, deleteOne, batchDelete } from './erplist.api';
//弹窗model
const [registerModal, { openModal }] = useModal();
// 列表页面公共参数、方法
const { tableContext } = useListPage({
tableProps: {
api: list,
tableSetting:{
cacheKey:'erp_main'
},
columns: columns,
canResize: false,
rowSelection: { type: 'radio' },
formConfig: {
schemas: searchFormSchema,
},
actionColumn: {
width: 180,
},
pagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20'],
},
},
});
//注册table数据
const [registerTable, { reload, updateTableDataRecord }, { rowSelection, selectedRowKeys }] = tableContext;
const orderId = computed(() => (unref(selectedRowKeys).length > 0 ? unref(selectedRowKeys)[0] : ''));
//下发 orderId,子组件接收
provide('orderId', orderId);
/**
* 新增事件
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
async function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
async function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, () => {
selectedRowKeys.value = [];
reload();
});
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
];
}
</script>
<style scoped></style>

View File

@ -0,0 +1,54 @@
<template>
<div class="p-4">
<a-card :bordered="false" style="height: 100%">
<a-tabs v-model:activeKey="activeKey" @change="tabChange">
<a-tab-pane key="JeecgComponents" tab="下拉选择组件"></a-tab-pane>
<a-tab-pane key="JCodeEditDemo" tab="代码编辑器" force-render></a-tab-pane>
<a-tab-pane key="JEditorDemo" tab="富文本&MakeDown"></a-tab-pane>
<a-tab-pane key="ImgDragSort" tab="图片拖拽"></a-tab-pane>
<a-tab-pane key="ImgTurnPage" tab="图片翻页"></a-tab-pane>
<a-tab-pane key="JeecgPdfView" tab="PDF预览"></a-tab-pane>
<a-tab-pane key="JUploadDemo" tab="文件上传"></a-tab-pane>
</a-tabs>
<component :is="currentComponent"></component>
</a-card>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed } from 'vue';
import JeecgComponents from './JeecgComponents.vue';
import JEditorDemo from './JEditorDemo.vue';
import JCodeEditDemo from './JCodeEditDemo.vue';
import ImgDragSort from './ImgDragSort.vue';
import ImgTurnPage from './ImgTurnPage.vue';
import JeecgPdfView from './JeecgPdfView.vue';
import JUploadDemo from './JUploadDemo.vue';
export default defineComponent({
name: 'comp-jeecg-basic',
setup() {
const activeKey = ref('JeecgComponents');
const currentComponent = computed(() => {
const componentType = {
JeecgComponents: JeecgComponents,
JEditorDemo: JEditorDemo,
JCodeEditDemo: JCodeEditDemo,
ImgDragSort: ImgDragSort,
ImgTurnPage: ImgTurnPage,
JeecgPdfView: JeecgPdfView,
JUploadDemo: JUploadDemo,
};
return componentType[activeKey.value];
});
//使用component动态切换tab
function tabChange(key) {
activeKey.value = key;
}
return {
activeKey,
currentComponent,
tabChange,
};
},
});
</script>

View File

@ -0,0 +1,864 @@
import { FormSchema, JCronValidator } from '/@/components/Form';
import { usePermission } from '/@/hooks/web/usePermission';
const { isDisabledAuth } = usePermission();
export const schemas: FormSchema[] = [
{
field: 'jdst',
component: 'JDictSelectTag',
label: '性别下拉',
helpMessage: ['component模式'],
componentProps: {
dictCode: 'sex',
},
colProps: {
span: 12,
},
},
{
field: 'jdst',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'jdst1',
component: 'JDictSelectTag',
label: '性别选择',
helpMessage: ['component模式'],
componentProps: {
dictCode: 'sex',
type: 'radioButton',
},
colProps: {
span: 12,
},
},
{
field: 'jdst1',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'jdst2',
component: 'JDictSelectTag',
label: '字典表下拉',
helpMessage: ['component模式'],
componentProps: {
dictCode: 'sys_user,realname,id',
},
colProps: {
span: 12,
},
},
{
field: 'jdst2',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'jdst3',
component: 'JDictSelectTag',
label: '字典表下拉(带条件)',
helpMessage: ['component模式'],
componentProps: {
dictCode: "sys_user,realname,id,username!='admin' order by create_time",
},
colProps: {
span: 12,
},
},
{
field: 'jdst3',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'jsst',
component: 'JSearchSelect',
label: '字典搜索(同步)',
colProps: { span: 12 },
componentProps: {
//dict: "sys_depart,depart_name,id",
dictOptions: [
{
text: '选项一',
value: '1',
},
{
text: '选项二',
value: '2',
},
{
text: '选项三',
value: '3',
},
],
},
},
{
field: 'jsst',
component: 'JEllipsis',
label: '选择值',
colProps: { span: 12 },
},
{
field: 'jsst2',
component: 'JSearchSelect',
label: '字典搜索(异步)',
colProps: { span: 12 },
componentProps: {
dict: 'sys_depart,depart_name,id',
pageSize: 6,
async: true,
},
},
{
field: 'jsst2',
component: 'JEllipsis',
label: '选择值',
colProps: { span: 12 },
},
{
field: 'xldx',
component: 'JDictSelectTag',
label: '字典下拉多选',
colProps: { span: 12 },
componentProps: {
dictCode: 'sex',
mode: 'multiple',
},
},
{
field: 'xldx',
component: 'JEllipsis',
label: '选择值',
colProps: { span: 12 },
},
{
field: 'xldx2',
component: 'JSelectMultiple',
label: '字典下拉多选2',
colProps: { span: 12 },
componentProps: {
dictCode: 'sex',
},
},
{
field: 'xldx2',
component: 'JEllipsis',
label: '选择值',
colProps: { span: 12 },
},
{
field: 'dxxlk',
component: 'JDictSelectTag',
label: '字典下拉单选',
colProps: { span: 12 },
componentProps: {
dictCode: 'sex',
},
},
{
field: 'dxxlk',
component: 'JEllipsis',
label: '选择值',
colProps: { span: 12 },
},
{
label: '可输入下拉',
field: 'selectInput',
component: 'JSelectInput',
componentProps: {
options: [
{ label: '选项一', value: '1' },
{ label: '选项二', value: '2' },
{ label: '选项三', value: '3' },
],
},
colProps: { span: 12 },
},
{
field: 'selectInput',
component: 'JEllipsis',
label: '选择值',
colProps: { span: 12 },
},
{
field: 'depart3',
component: 'JSelectDept',
label: '选择部门—自定义值',
helpMessage: ['component模式'],
componentProps: { showButton: false, rowKey: 'orgCode', primaryKey: 'orgCode' },
colProps: {
span: 12,
},
},
{
field: 'depart3',
component: 'JEllipsis',
label: '选中部门',
colProps: { span: 12 },
},
{
field: 'depart2',
component: 'JSelectDept',
label: '选择部门',
helpMessage: ['component模式'],
componentProps: { showButton: false },
colProps: {
span: 12,
},
},
{
field: 'depart2',
component: 'JEllipsis',
label: '选中部门',
colProps: { span: 12 },
},
{
field: 'user2',
component: 'JSelectUser',
label: '用户选择组件',
helpMessage: ['component模式'],
componentProps: {
labelKey: 'realname',
rowKey: 'id',
showSelected: true,
},
colProps: {
span: 12,
},
},
{
field: 'user2',
component: 'JEllipsis',
label: '选中用户',
colProps: { span: 12 },
},
{
field: 'user3',
component: 'JSelectUserByDept',
label: '部门选择用户',
helpMessage: ['component模式'],
componentProps: {
labelKey: 'realname',
rowKey: 'username',
},
colProps: {
span: 12,
},
},
{
field: 'user3',
component: 'JEllipsis',
label: '选中用户',
colProps: { span: 12 },
},
{
field: 'role2',
component: 'JSelectRole',
label: '角色选择组件',
helpMessage: ['component模式'],
colProps: {
span: 12,
},
},
{
field: 'role2',
component: 'JEllipsis',
label: '选中角色',
colProps: { span: 12 },
},
{
field: 'position2',
component: 'JSelectPosition',
label: '职务选择组件',
helpMessage: ['component模式'],
colProps: { span: 12 },
componentProps: { async: true, showSelectTable: true },
},
{
field: 'position2',
component: 'JEllipsis',
label: '选中职务',
colProps: { span: 12 },
},
{
field: 'checkbox1',
component: 'JCheckbox',
label: 'JCheckbox组件1',
helpMessage: ['component模式'],
defaultValue: '1,2',
componentProps: {
options: [
{ label: '男', value: '1' },
{ label: '女', value: '2' },
],
},
colProps: {
span: 12,
},
},
{
field: 'checkbox1',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'checkbox2',
component: 'Input',
label: 'JCheckbox组件2',
defaultValue: '1',
helpMessage: ['插槽模式'],
slot: 'JCheckbox',
colProps: {
span: 12,
},
},
{
field: 'checkbox2',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'data1',
label: '日期选择',
component: 'DatePicker',
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
},
colProps: {
span: 12,
},
},
{
field: 'data1',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'data2',
label: '年份范围选择',
component: 'RangePicker',
componentProps: {
picker: 'year',
valueFormat: 'YYYY',
},
colProps: {
span: 12,
},
},
{
field: 'data2',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'hk',
component: 'Input',
label: '滑块验证码',
helpMessage: ['插槽模式'],
slot: 'dargVerify',
colProps: {
span: 12,
},
},
{
field: 'hk',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'JTreeDict',
component: 'JTreeDict',
label: '树字典',
helpMessage: ['component模式'],
colProps: { span: 12 },
},
{
field: 'JTreeDict',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'ts',
component: 'JTreeSelect',
label: '下拉树选择',
helpMessage: ['component模式'],
componentProps: {
dict: 'sys_permission,name,id',
pidField: 'parent_id',
hasChildField: 'is_leaf',
converIsLeafVal: 0,
},
colProps: {
span: 12,
},
},
{
field: 'ts',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'ts1',
component: 'JTreeSelect',
label: '下拉树多选',
helpMessage: ['component模式'],
componentProps: {
dict: 'sys_permission,name,id',
pidField: 'parent_id',
hasChildField: 'is_leaf',
converIsLeafVal: 0,
multiple: true,
},
colProps: {
span: 12,
},
},
{
field: 'ts1',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'category',
component: 'JCategorySelect',
label: '分类字典树',
helpMessage: ['component模式'],
defaultValue: '',
componentProps: {
pcode: 'B01',
multiple: true,
},
colProps: {
span: 12,
},
},
{
field: 'category',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'JEasyCron',
component: 'JEasyCron',
label: 'JEasyCron',
helpMessage: ['component模式'],
colProps: { span: 12 },
defaultValue: '* * * * * ? *',
rules: [{ validator: JCronValidator }],
},
{
field: 'JEasyCron',
component: 'JEllipsis',
label: '选择值',
colProps: { span: 12 },
},
{
field: 'JInput',
component: 'JInput',
label: '特殊查询组件',
helpMessage: ['插槽模式'],
slot: 'JInput',
colProps: {
span: 12,
},
},
{
field: 'jinputtype',
component: 'Select',
label: '查询类型',
componentProps: {
options: [
{ value: 'like', label: '模糊like' },
{ value: 'ne', label: '不等于ne' },
{ value: 'ge', label: '大于等于ge' },
{ value: 'le', label: '小于等于le)' },
],
},
colProps: {
span: 6,
},
},
{
field: 'JInput',
component: 'JEllipsis',
label: '输入值',
colProps: { span: 6 },
},
{
field: 'field1',
component: 'Select',
label: '省市区选择',
helpMessage: ['插槽模式'],
slot: 'jAreaLinkage',
colProps: {
span: 12,
},
defaultValue: ['130000', '130200'],
},
{
field: 'field1',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'field0',
component: 'Select',
label: '禁用组件(方式一)',
helpMessage: ['插槽模式'],
slot: 'jAreaLinkage1',
colProps: {
span: 12,
},
defaultValue: ['130000', '130200'],
},
{
field: 'field0',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'field2',
component: 'JAreaLinkage',
label: '禁用组件(方式二)',
helpMessage: ['component模式'],
colProps: {
span: 12,
},
dynamicDisabled: ({ values }) => {
console.log(values);
return isDisabledAuth(['demo.dbarray']);
},
defaultValue: ['140000', '140300', '140302'],
},
{
field: 'field2',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'pca1',
component: 'JAreaSelect',
label: '省市区级联',
helpMessage: ['component模式'],
defaultValue: '140302',
colProps: {
span: 12,
},
},
{
field: 'pca1',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'pop1',
component: 'Input',
label: 'JPopup示例',
helpMessage: ['插槽模式'],
slot: 'JPopup',
colProps: {
span: 12,
},
},
{
field: 'pop1',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'JInputPop',
component: 'JInputPop',
label: 'JInputPop',
helpMessage: ['component模式'],
colProps: { span: 12 },
},
{
field: 'JInputPop',
component: 'JEllipsis',
label: '输入值',
colProps: { span: 12 },
},
{
field: 'JTreeDictAsync',
component: 'JTreeDict',
label: '异步JTreeDict',
helpMessage: ['component模式'],
colProps: { span: 12 },
componentProps: { async: true },
},
{
field: 'JTreeDictAsync',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'JSwitch',
component: 'JSwitch',
label: 'JSwitch',
helpMessage: ['component模式'],
colProps: { span: 12 },
},
{
field: 'JSwitch',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'JSwitchSelect',
component: 'JSwitch',
label: 'JSwitchSelect',
helpMessage: ['component模式'],
colProps: { span: 12 },
componentProps: { query: true },
},
{
field: 'JSwitchSelect',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'userSelect2',
component: 'UserSelect',
label: '高级用户选择',
helpMessage: ['component模式'],
colProps: { span: 12 },
},
{
field: 'userSelect2',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'superQuery',
component: 'Input',
label: '高级查询',
helpMessage: ['插槽模式'],
slot: 'superQuery',
colProps: { span: 12 },
},
{
field: 'superQuery',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'superQuery1',
component: 'Input',
label: '高级查询',
helpMessage: ['插槽模式-自己保存查询条件'],
slot: 'superQuery1',
colProps: { span: 12 },
},
{
field: 'superQuery1',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'pop2',
component: 'JPopupDict',
label: 'JPopupDict示例',
colProps: {
span: 12,
},
componentProps:{
placeholder: '请选择',
dictCode: 'report_user,username,id',
multi: true,
},
},
{
field: 'pop2',
component: 'JEllipsis',
label: '选中值',
colProps: {
span: 12,
},
},
{
field: 'sex',
component: 'JDictSelectTag',
label: '性别(控制下方课程options)',
helpMessage: ['component模式','性别不同,下方课程展示选项不同'],
componentProps: {
dictCode: 'sex',
type: 'radioButton',
onChange: (value) => {
console.log(value);
},
},
colProps: {
span: 12,
},
},
{
field: 'sex',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'course',
component: 'Select',
label: '课程',
dynamicPropskey: 'options',
dynamicPropsVal: ({ model }) => {
let options;
if (model.sex == 1) {
return [
{ value: '0', label: 'java - 男' },
{ value: '1', label: 'vue - 男' },
];
} else if (model.sex == 2) {
return [
{ value: '2', label: '瑜伽 - 女' },
{ value: '3', label: '美甲 - 女' },
];
} else {
return [];
}
},
componentProps: {
disabled: false,
},
colProps: {
span: 12,
},
},
{
field: 'course',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'field100',
component: 'JInputSelect',
label: 'JInputSelect',
helpMessage: ['component模式'],
componentProps: {
selectPlaceholder: '可选择系统变量',
inputPlaceholder: '请输入',
options: [
{
label: '登录用户账号',
value: '${sys_user_code}',
},
{
label: '登录用户名称',
value: '${sys_user_name}',
},
{
label: '当前日期',
value: '${sys_date}',
},
{
label: '当前时间',
value: '${sys_date}',
},
{
label: '登录用户部门',
value: '${sys_org_code}',
},
{
label: '用户拥有部门',
value: '${sys_multi_org_code}',
},
{
label: '登录用户租户',
value: '${tenant_id}',
},
],
},
colProps: {
span: 12,
},
},
{
field: 'field100',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'JAreaLinkage',
component: 'JAreaLinkage',
label: '省市区选择',
colProps: {
span: 12,
},
},
{
field: 'JAreaLinkage',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
{
field: 'orderAuth',
component: 'Input',
label: '指令权限',
helpMessage: ['有权限右侧的"选中值"可见,否则不可见'],
colProps: {
span: 12,
},
},
{
field: 'orderAuth',
auth: 'demo:order:auth',
component: 'JEllipsis',
label: '选中值',
colProps: { span: 12 },
},
];

View File

@ -0,0 +1,94 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="700px">
<BasicForm @register="registerForm" />
<!--TODO 子表Tab数据-->
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { defHttp } from '/@/utils/http/axios';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 150,
schemas: [
{
field: 'orderCode',
label: '订单号',
component: 'Input',
required: true,
},
{
field: 'ctype',
label: '订单类型',
component: 'Select',
componentProps: {
options: [
{ label: '国内订单', value: '1' },
{ label: '国际订单', value: '2' },
],
},
},
{
field: 'orderDate',
label: '订单日期',
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD hh:mm:ss',
},
},
{
field: 'orderMoney',
label: '订单金额',
component: 'InputNumber',
},
{
field: 'content',
label: '订单备注',
component: 'Input',
},
{
field: 'id',
label: 'id',
component: 'Input',
show: false,
},
],
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增订单' : '编辑订单'));
//表单提交事件
async function handleSubmit(v) {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
//提交表单
let url = unref(isUpdate) ? '/test/order/edit' : '/test/order/add';
defHttp.post({ url: url, params: values });
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>