前端和后端源码,合并到一个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,33 @@
import { registerComponent, registerAsyncComponent, registerASyncComponentReal } from '/@/components/jeecg/JVxeTable';
import { JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
import { DictSearchSpanCell, DictSearchInputCell } from './src/components/JVxeSelectDictSearchCell';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
export async function registerJVxeCustom() {
// ----------------- ⚠ 注意事项 ⚠ -----------------
// 当组件内包含 BasicModal 时,必须使用异步引入!
// 否则将会导致 i18n 失效!
// ----------------- ⚠ 注意事项 ⚠ -----------------
// 注册【Popup】普通封装方式
await registerAsyncComponent(JVxeTypes.popup, import('./src/components/JVxePopupCell.vue'));
// 注册【字典搜索下拉】组件(高级封装方式)
registerComponent(JVxeTypes.selectDictSearch, DictSearchInputCell, DictSearchSpanCell);
// 注册【文件上传】组件
await registerAsyncComponent(JVxeTypes.file, import('./src/components/JVxeFileCell.vue'));
// 注册【图片上传】组件
await registerAsyncComponent(JVxeTypes.image, import('./src/components/JVxeImageCell.vue'));
// 注册【用户选择】组件
await registerAsyncComponent(JVxeTypes.userSelect, import('./src/components/JVxeUserSelectCell.vue'));
// 注册【部门选择】组件
await registerAsyncComponent(JVxeTypes.departSelect, import('./src/components/JVxeDepartSelectCell.vue'));
// 注册【省市区选择】组件
// await registerAsyncComponent(JVxeTypes.pca, import('./src/components/JVxePcaCell.vue'));
// update-begin--author:liaozhiyang---date:20240308---for【QQYUN-8241】为避免首次加载china-area-dataJVxePcaCell组件需异步加载
registerASyncComponentReal(
JVxeTypes.pca,
createAsyncComponent(() => import('./src/components/JVxePcaCell.vue'))
);
// update-end--author:liaozhiyang---date:20240308---for【QQYUN-8241】为避免首次加载china-area-dataJVxePcaCell组件需异步加载
}

View File

@ -0,0 +1,213 @@
<template>
<!-- <div>-->
<!-- <a-input-->
<!-- v-show="!departIds"-->
<!-- @click="openSelect"-->
<!-- placeholder="请点击选择部门"-->
<!-- v-model="departNames"-->
<!-- readOnly-->
<!-- :disabled="componentDisabled"-->
<!-- class="jvxe-select-input">-->
<!-- <a-icon slot="prefix" type="cluster" title="部门选择控件"/>-->
<!-- </a-input>-->
<!-- <j-select-depart-modal-->
<!-- ref="innerDepartSelectModal"-->
<!-- :modal-width="modalWidth"-->
<!-- :multi="multi"-->
<!-- :rootOpened="rootOpened"-->
<!-- :depart-id="departIds"-->
<!-- @ok="handleOK"-->
<!-- @initComp="initComp"/>-->
<!-- <span style="display: inline-block;height:100%;padding-left:14px" v-if="departIds">-->
<!-- <span @click="openSelect" style="display: inline-block;vertical-align: middle">{{ departNames }}</span>-->
<!-- <a-icon style="margin-left:5px;vertical-align: middle" type="close-circle" @click="handleEmpty" title="清空"/>-->
<!-- </span>-->
<!-- </div>-->
<div :class="[prefixCls]">
<JSelectDept v-bind="getProps" @change="handleChange" />
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
//import { JSelectDept } from '/@/components/Form';
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
// import JSelectDepartModal from '@/components/jeecgbiz/modal/JSelectDepartModal'
import { dispatchEvent } from '/@/components/jeecg/JVxeTable/utils';
import { isArray, isEmpty, isString } from '/@/utils/is';
export default defineComponent({
name: 'JVxeDepartSelectCell',
components: { JSelectDept },
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { innerValue, cellProps, handleChangeCommon, useCellDesign } = useJVxeComponent(props);
const { prefixCls } = useCellDesign('depart-select');
const selectedValue = computed(() => {
let val: any = innerValue.value;
if (val == null) {
return val;
}
if (isEmpty(val)) {
return [];
}
if (isArray(val)) {
return val;
}
if (isString(val)) {
return (<string>val).split(',');
}
return [val];
});
const multiple = computed(() => cellProps.value['multi'] != false);
const getProps = computed(() => {
return {
...cellProps.value,
value: selectedValue.value,
showButton: false,
// 不允许搜索
showSearch: false,
// 设置最大的显示个数
maxTagCount: 1,
// 显示提示重写,去掉省略号
maxTagPlaceholder: ({ length }) => '+' + length,
};
});
function handleChange(values) {
handleChangeCommon(values.join(','));
}
return {
prefixCls,
selectedValue,
multiple,
cellProps,
getProps,
handleChange,
};
},
data() {
return {
// departNames: '',
// departIds: '',
// selectedOptions: [],
// customReturnField: 'id',
};
},
computed: {
// custProps() {
// const { departIds, originColumn: col, caseId, cellProps } = this
// return {
// ...cellProps,
// value: departIds,
// field: col.field || col.key,
// groupId: caseId,
// class: 'jvxe-select',
// }
// },
// componentDisabled() {
// if (this.cellProps.disabled == true) {
// return true
// }
// return false
// },
// modalWidth() {
// if (this.cellProps.modalWidth) {
// return this.cellProps.modalWidth
// } else {
// return 500
// }
// },
// multi() {
// if (this.cellProps.multi == false) {
// return false
// } else {
// return true
// }
// },
// rootOpened() {
// if (this.cellProps.open == false) {
// return false
// } else {
// return true
// }
// },
},
watch: {
// innerValue: {
// immediate: true,
// handler(val) {
// if (val == null || val === '') {
// this.departIds = ''
// } else {
// this.departIds = val
// }
// },
// },
},
methods: {
// openSelect() {
// this.$refs.innerDepartSelectModal.show()
// },
// handleEmpty() {
// this.handleOK('')
// },
// handleOK(rows, idstr) {
// let value = ''
// if (!rows && rows.length <= 0) {
// this.departNames = ''
// this.departIds = ''
// } else {
// value = rows.map(row => row[this.customReturnField]).join(',')
// this.departNames = rows.map(row => row['departName']).join(',')
// this.departIds = idstr
// }
// this.handleChangeCommon(this.departIds)
// },
// initComp(departNames) {
// this.departNames = departNames
// },
// handleChange(value) {
// this.handleChangeCommon(value)
// },
},
enhanced: {
switches: {
visible: true,
},
translate: {
enabled: false,
},
aopEvents: {
editActived({ $event }) {
dispatchEvent({
$event,
props: this.props,
className: '.ant-select .ant-select-selection-search-input',
isClick: true,
});
},
},
} as JVxeComponent.EnhancedPartial,
});
</script>
<style lang="less">
// noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-vxe-cell-depart-select';
.@{prefix-cls} {
// 限制tag最大长度为100px防止选中文字过多的选项时换行
.ant-select .ant-select-selection-overflow-item {
max-width: 80px;
}
}
</style>

View File

@ -0,0 +1,78 @@
<template>
<div>
<template v-if="hasFile" v-for="(file, fileKey) of [innerFile || {}]" :key="fileKey">
<div style="position: relative">
<a-tooltip v-if="file.status === 'uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
<LoadingOutlined />
<span style="margin-left: 5px">上传中</span>
</a-tooltip>
<a-tooltip v-else-if="file.status === 'done'" :title="file.name">
<Icon icon="ant-design:paper-clip" />
<span style="margin-left: 5px">{{ ellipsisFileName }}</span>
</a-tooltip>
<a-tooltip v-else :title="file.message || '上传失败'">
<Icon icon="ant-design:exclamation-circle" style="color: red" />
<span style="margin-left: 5px">{{ ellipsisFileName }}</span>
</a-tooltip>
<Dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px">
<a-tooltip title="操作">
<Icon v-if="file.status !== 'uploading'" icon="ant-design:setting" style="cursor: pointer" />
</a-tooltip>
<template #overlay>
<a-menu>
<a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
<span><Icon icon="ant-design:download" />&nbsp;下载</span>
</a-menu-item>
<a-menu-item :disabled="cellProps.disabled" v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
<span><Icon icon="ant-design:delete" />&nbsp;删除</span>
</a-menu-item>
<a-menu-item :disabled="cellProps.disabled" @click="handleMoreOperation">
<span><Icon icon="ant-design:bars" />&nbsp;更多</span>
</a-menu-item>
</a-menu>
</template>
</Dropdown>
</div>
</template>
<a-upload
v-if="!cellProps.disabledTable"
v-show="!hasFile"
name="file"
:data="{ isup: 1 }"
:multiple="false"
:action="uploadAction"
:headers="uploadHeaders"
:showUploadList="false"
v-bind="cellProps"
@change="handleChangeUpload"
>
<a-button preIcon="ant-design:upload">{{ originColumn.btnText || '点击上传' }}</a-button>
</a-upload>
<JUploadModal :value="modalValue" @register="registerModel" @change="onModalChange" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { UploadTypeEnum } from '/@/components/Form/src/jeecg/components/JUpload';
import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
import { useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
import { useFileCell, enhanced, components } from '../hooks/useFileCell';
export default defineComponent({
name: 'JVxeFileCell',
components: components,
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
return useFileCell(props, UploadTypeEnum.file);
},
// 【组件增强】注释详见JVxeComponent.Enhanced
enhanced: enhanced,
});
</script>
<style scoped lang="less"></style>

View File

@ -0,0 +1,148 @@
<template>
<div>
<template v-if="hasFile" v-for="(file, fileKey) of [innerFile || {}]" :key="fileKey">
<div class="j-vxe-image-list">
<template v-if="!file || !(file['url'] || file['path'] || file['message'])">
<a-tooltip :title="'请稍后: ' + JSON.stringify(file) + (file['url'] || file['path'] || file['message'])">
<LoadingOutlined />
</a-tooltip>
</template>
<template v-else-if="file['path']">
<template v-for="src of imgList">
<img class="j-vxe-image" :src="src" alt="图片错误" @[clickEvent]="handleMoreOperation" @click="handlePreview" />
</template>
</template>
<a-tooltip v-else :title="file.message || '上传失败'" @[clickEvent]="handleClickShowImageError">
<Icon icon="ant-design:exclamation-circle" style="color: red" />
</a-tooltip>
</div>
</template>
<div class="j-vxe-image-upload" v-if="!cellProps.disabledTable">
<a-upload
:accept="acceptFileType"
name="file"
:data="{ isup: 1 }"
:multiple="false"
:action="uploadAction"
:headers="uploadHeaders"
:showUploadList="false"
v-bind="cellProps"
@change="handleChangeUpload"
>
<a-button v-if="!hasFile" preIcon="ant-design:upload">{{ originColumn.btnText || '上传图片' }}</a-button>
<div v-if="hasFile && imgList.length < maxCount" class="j-vxe-plus" @click="">
<Icon icon="ant-design:plus" />
</div>
</a-upload>
</div>
<JUploadModal :value="modalValue" @register="registerModel" @change="onModalChange" />
</div>
</template>
<script lang="ts">
import { computed, defineComponent ,unref } from 'vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
import { useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
import { UploadTypeEnum } from '/@/components/Form/src/jeecg/components/JUpload';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { components, enhanced, useFileCell } from '../hooks/useFileCell';
import { createImgPreview } from '/@/components/Preview/index';
export default defineComponent({
name: 'JVxeImageCell',
components: components,
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { createErrorModal } = useMessage();
const setup = useFileCell(props, UploadTypeEnum.image, { multiple: true });
// update-begin--author:liaozhiyang---date:20240604---for【TV360X-470】jVxetable上传图片组件限制类型
const acceptFileType = 'image/*';
// update-end--author:liaozhiyang---date:20240604---for【TV360X-470】jVxetable上传图片组件限制类型
// update-begin--author:liaozhiyang---date:20240105---for【issues/953】online子表vxe-table展现形式详情图片上传可点击
const clickEvent = computed(() => {
return unref(setup.cellProps).disabled ? null : 'click';
});
// update-end--author:liaozhiyang---date:20240105---for【issues/953】online子表vxe-table展现形式详情图片上传可点击
const { innerFile, maxCount } = setup;
const imgList = computed(() => {
if (innerFile.value) {
if (innerFile.value['url']) {
return [innerFile.value['url']];
} else if (innerFile.value['path']) {
let paths = innerFile.value['path'].split(',');
return paths.map((p) => getFileAccessHttpUrl(p));
}
}
return [];
});
// 弹出上传出错详细信息
function handleClickShowImageError() {
let file = innerFile.value || null;
if (file && file['message']) {
createErrorModal({
title: '上传出错',
content: '错误信息:' + file['message'],
maskClosable: true,
});
}
}
// update-begin--author:liaozhiyang---date:20240523---for【TV360X-121】jvxetable文件组件禁用状态(详情)下可下载
const handlePreview = () => {
if (unref(setup.cellProps).disabled) {
createImgPreview({ imageList: imgList.value });
}
};
// update-end--author:liaozhiyang---date:20240523---for【TV360X-121】jvxetable文件组件禁用状态(详情)下可下载
return {
...setup,
imgList,
maxCount,
handleClickShowImageError,
clickEvent,
handlePreview,
acceptFileType,
};
},
// 【组件增强】注释详见JVxeComponent.Enhanced
enhanced: enhanced,
});
</script>
<style scoped lang="less">
.j-vxe-image {
height: 32px;
max-width: 100px !important;
cursor: pointer;
display: inline-block;
margin-right: 4px;
vertical-align: top;
&:hover {
opacity: 0.8;
}
&:active {
opacity: 0.6;
}
}
.j-vxe-plus {
width: 32px;
height: 32px;
line-height: 32px;
text-align: center;
background-color: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 2px;
cursor: pointer;
}
.j-vxe-image-list,
.j-vxe-image-upload {
display: inline-block;
vertical-align: top;
}
</style>

View File

@ -0,0 +1,77 @@
<template>
<a-cascader v-bind="getProps" class="pca-select" @change="handleChange" />
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { regionData, getRealCode } from '/@/components/Form/src/utils/areaDataUtil';
import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
import { dispatchEvent } from '/@/components/jeecg/JVxeTable/utils';
export default defineComponent({
name: 'JVxePcaCell',
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { innerValue, cellProps, handleChangeCommon } = useJVxeComponent(props);
const selectedValue = computed(() => {
let val: any = innerValue.value;
if (!val) {
return []
}
let arr = getRealCode(val, 3);
return arr;
});
const getProps = computed(() => {
return {
...cellProps.value,
options: regionData,
showOverflow: false,
value: selectedValue.value,
};
});
function handleChange(arr) {
let str = '';
if(arr && arr.length==3){
str = arr[2];
}
handleChangeCommon(str);
}
return {
handleChange,
selectedValue,
getProps
};
},
// 【组件增强】注释详见JVxeComponent.Enhanced
enhanced: {
switches: {
visible: true,
},
translate: {
enabled: false,
},
aopEvents: {
editActived({ $event }) {
dispatchEvent({
$event,
props: this.props,
className: '.ant-select .ant-select-selection-search-input',
isClick: true,
});
},
},
} as JVxeComponent.EnhancedPartial,
});
</script>
<style lang="less">
.pca-select{
.ant-select-selection-placeholder{
color: #bfbfbf !important;
}
}
</style>

View File

@ -0,0 +1,75 @@
<template>
<JPopup v-bind="popupProps" @focus="handleFocus" />
</template>
<script lang="ts">
import { computed, defineComponent, ref } from 'vue';
//import { JPopup } from '/@/components/Form';
import JPopup from '/@/components/Form/src/jeecg/components/JPopup.vue';
import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
import { dispatchEvent, vModel } from '/@/components/jeecg/JVxeTable/utils';
import { isEmpty } from '/@/utils/is';
export default defineComponent({
name: 'JVxePopupCell',
components: { JPopup },
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { innerValue, row, originColumn, cellProps, handleChangeCommon } = useJVxeComponent(props);
const groupId = ref<string>('j-vxe-popup');
const popupProps = computed(() => {
// update-begin--author:liaozhiyang---date:20231009---for【issues/5371】一对多子表popup增加多选
return {
...cellProps.value,
value: innerValue.value,
field: originColumn.value.field || originColumn.value.key,
code: originColumn.value.popupCode,
fieldConfig: originColumn.value.fieldConfig,
// orgFields: originColumn.value.orgFields,
// destFields: originColumn.value.destFields,
groupId: groupId.value,
param: originColumn.value.params,
sorter: originColumn.value.sorter,
setFieldsValue: (values) => {
if (!isEmpty(values)) {
let popupValue = '';
Object.keys(values).forEach((key) => {
let currentValue = values[key];
// 当前列直接赋值其他列通过vModel赋值
if (key === originColumn.value.key) {
popupValue = currentValue;
} else {
vModel(currentValue, row, key);
}
});
handleChangeCommon(popupValue);
}
},
};
// update-end--author:liaozhiyang---date:20231009---for【issues/5371】一对多子表popup增加多选
});
// update-begin--author:liaozhiyang---date:20230811---for【issues/675】子表字段Popup弹框数据不更新
const handleFocus = () => {
groupId.value = '';
};
// update-end--author:liaozhiyang---date:20230811---for【issues/675】子表字段Popup弹框数据不更新
return {
handleFocus,
popupProps,
};
},
// 【组件增强】注释详见JVxeComponent.Enhanced
enhanced: {
aopEvents: {
editActived({ $event }) {
dispatchEvent({
$event,
props: this.props,
className: '.ant-input',
isClick: true,
});
},
},
} as JVxeComponent.EnhancedPartial,
});
</script>

View File

@ -0,0 +1,288 @@
import { computed, ref, watch, defineComponent, h } from 'vue';
import { cloneDeep, debounce } from 'lodash-es';
import { defHttp } from '/@/utils/http/axios';
import { filterDictText } from '/@/utils/dict/JDictSelectUtil';
import { ajaxGetDictItems, getDictItemsByCode } from '/@/utils/dict';
import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
import { dispatchEvent } from '/@/components/jeecg/JVxeTable/utils';
import { useResolveComponent as rc } from '/@/components/jeecg/JVxeTable/hooks';
import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
import { useMessage } from '/@/hooks/web/useMessage';
/** value - label map防止重复查询刷新清空缓存 */
const LabelMap = new Map<string, any>();
// 请求id
let requestId = 0;
/** 显示组件,自带翻译 */
export const DictSearchSpanCell = defineComponent({
name: 'JVxeSelectSearchSpanCell',
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { innerOptions, innerSelectValue, innerValue } = useSelectDictSearch(props);
return () => {
return h('span', {}, [filterDictText(innerOptions.value, innerSelectValue.value || innerValue.value)]);
};
},
});
// 输入选择组件
export const DictSearchInputCell = defineComponent({
name: 'JVxeSelectSearchInputCell',
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { createMessage } = useMessage();
const { dict, loading, isAsync, options, innerOptions, originColumn, cellProps, innerSelectValue, handleChangeCommon } =
useSelectDictSearch(props);
const hasRequest = ref(false);
// 提示信息
const tipsContent = computed(() => {
return originColumn.value.tipsContent || '请输入搜索内容';
});
// 筛选函数
const filterOption = computed(() => {
if (isAsync.value) {
//【jeecgboot-vue3/issues/I5QRT8】JVxeTypes.selectDictSearch sync问题
return ()=>true;
}
return (input, option) => option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0;
});
/** 加载数据 */
const loadData = debounce((value) => {
const currentRequestId = ++requestId;
loading.value = true;
innerOptions.value = [];
if (value == null || value.trim() === '') {
loading.value = false;
hasRequest.value = false;
return;
}
// 字典code格式table,text,code
hasRequest.value = true;
loadDictByKeyword(dict.value, value)
.then((res) => {
if (currentRequestId !== requestId) {
return;
}
let { success, result, message } = res;
if (success) {
innerOptions.value = result;
result.forEach((item) => {
LabelMap.set(item.value, [item]);
});
} else {
createMessage.warning(message || '查询失败');
}
})
.finally(() => {
loading.value = false;
});
}, 300);
function handleChange(selectedValue) {
innerSelectValue.value = selectedValue;
handleChangeCommon(innerSelectValue.value);
}
function handleSearch(value) {
if (isAsync.value) {
// 在输入时也应该开启加载因为loadData加了消抖所以会有800ms的用户主观上认为的卡顿时间
loading.value = true;
if (innerOptions.value.length > 0) {
innerOptions.value = [];
}
loadData(value);
}
}
function renderOptionItem() {
let optionItems: any[] = [];
options.value.forEach(({ value, text, label, title, disabled }) => {
optionItems.push(
h(
rc('a-select-option'),
{
key: value,
value: value,
disabled: disabled,
},
{
default: () => text || label || title,
}
)
);
});
return optionItems;
}
return () => {
return h(
rc('a-select'),
{
...cellProps.value,
value: innerSelectValue.value,
filterOption: filterOption.value,
showSearch: true,
allowClear: true,
autofocus: true,
defaultOpen: true,
style: 'width: 100%',
onSearch: handleSearch,
onChange: handleChange,
},
{
default: () => renderOptionItem(),
notFoundContent: () => {
if (loading.value) {
return h(rc('a-spin'), { size: 'small' });
} else if (hasRequest.value) {
return h('div', '没有查询到任何数据');
} else {
return h('div', [tipsContent.value]);
}
},
}
);
};
},
// 【组件增强】注释详见JVxeComponent.Enhanced
enhanced: {
aopEvents: {
editActived({ $event }) {
dispatchEvent({
$event,
props: this.props,
className: '.ant-select .ant-select-selection-search-input',
isClick: false,
handler: (el) => el.focus(),
});
},
},
} as JVxeComponent.EnhancedPartial,
});
function useSelectDictSearch(props) {
const setup = useJVxeComponent(props);
const { innerValue, originColumn } = setup;
// 加载状态
const loading = ref(false);
// 内部选择值
const innerSelectValue = ref(null);
// 内部 options
const innerOptions = ref<any[]>([]);
const dict = computed(() => originColumn.value.dict);
// 是否是异步模式
const isAsync = computed(() => {
let isAsync = originColumn.value.async;
return isAsync != null && isAsync !== '' ? !!isAsync : true;
});
const options = computed(() => {
if (isAsync.value) {
return innerOptions.value;
} else {
return originColumn.value.options || [];
}
});
/** 公共属性监听 */
watch(
innerValue,
(value: string) => {
if (value == null || value === '') {
innerSelectValue.value = null;
} else {
loadDataByValue(value);
}
},
{ immediate: true }
);
watch(dict, () => loadDataByDict());
// 根据 value 查询数据,用于回显
async function loadDataByValue(value) {
if (isAsync.value) {
if (innerSelectValue.value !== value) {
if (LabelMap.has(value)) {
innerOptions.value = cloneDeep(LabelMap.get(value));
} else {
let result = await loadDictItem(dict.value, value);
if (result && result.length > 0) {
innerOptions.value = [{ value: value, text: result[0] }];
LabelMap.set(value, cloneDeep(innerOptions.value));
}
}
}
}
innerSelectValue.value = (value || '').toString();
}
// 初始化字典
async function loadDataByDict() {
if (!isAsync.value) {
// 如果字典项集合有数据
if (!originColumn.value.options || originColumn.value.options.length === 0) {
// 根据字典Code, 初始化字典数组
let dictStr = '';
if (dict.value) {
let arr = dict.value.split(',');
if (arr[0].indexOf('where') > 0) {
let tbInfo = arr[0].split('where');
dictStr = tbInfo[0].trim() + ',' + arr[1] + ',' + arr[2] + ',' + encodeURIComponent(tbInfo[1]);
} else {
dictStr = dict.value;
}
if (dict.value.indexOf(',') === -1) {
//优先从缓存中读取字典配置
let cache = getDictItemsByCode(dict.value);
if (cache) {
innerOptions.value = cache;
return;
}
}
let { success, result } = await ajaxGetDictItems(dictStr, null);
if (success) {
innerOptions.value = result;
}
}
}
}
}
return {
...setup,
loading,
innerOptions,
innerSelectValue,
dict,
isAsync,
options,
};
}
/** 获取字典项 */
function loadDictItem(dict: string, key: string) {
return defHttp.get({
url: `/sys/dict/loadDictItem/${dict}`,
params: {
key: key,
},
});
}
/** 根据关键字获取字典项(搜索) */
function loadDictByKeyword(dict: string, keyword: string) {
return defHttp.get(
{
url: `/sys/dict/loadDict/${dict}`,
params: {
keyword: keyword,
},
},
{
isTransformResponse: false,
}
);
}

View File

@ -0,0 +1,102 @@
<template>
<div :class="[prefixCls]">
<JSelectUser v-bind="getProps" @change="handleChange" />
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
//import { JSelectUser } from '/@/components/Form';
import JSelectUser from '/@/components/Form/src/jeecg/components/JSelectUser.vue';
import { JVxeComponent } from '/@/components/jeecg/JVxeTable/types';
import { useJVxeComponent, useJVxeCompProps } from '/@/components/jeecg/JVxeTable/hooks';
import { dispatchEvent } from '/@/components/jeecg/JVxeTable/utils';
import { isArray, isEmpty, isString } from '/@/utils/is';
export default defineComponent({
name: 'JVxeUserSelectCell',
components: { JSelectUser },
props: useJVxeCompProps(),
setup(props: JVxeComponent.Props) {
const { innerValue, cellProps, handleChangeCommon, useCellDesign } = useJVxeComponent(props);
const { prefixCls } = useCellDesign('user-select');
const selectedValue = computed(() => {
let val: any = innerValue.value;
if (val == null) {
return val;
}
if (isEmpty(val)) {
return [];
}
if (isArray(val)) {
return val;
}
if (isString(val)) {
// @ts-ignore
return val.split(',');
}
return [val];
});
// @ts-ignore
const multiple = computed(() => cellProps.value.multi != false);
const getProps = computed(() => {
return {
...cellProps.value,
value: selectedValue.value,
showButton: false,
// 不允许搜索
showSearch: false,
// 设置最大的显示个数
maxTagCount: 1,
// 显示提示重写,去掉省略号
maxTagPlaceholder: ({ length }) => '+' + length,
};
});
function handleChange(values) {
handleChangeCommon(values.join(','));
}
return {
prefixCls,
selectedValue,
multiple,
cellProps,
getProps,
handleChange,
};
},
enhanced: {
switches: {
visible: true,
},
translate: {
enabled: false,
},
aopEvents: {
editActived({ $event }) {
dispatchEvent({
$event,
props: this.props,
className: '.ant-select .ant-select-selection-search-input',
isClick: true,
});
},
},
} as JVxeComponent.EnhancedPartial,
});
</script>
<style lang="less">
// noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-vxe-cell-user-select';
.@{prefix-cls} {
// 限制tag最大长度为100px防止选中文字过多的选项时换行
.ant-select .ant-select-selection-overflow-item {
max-width: 80px;
}
}
</style>

View File

@ -0,0 +1,102 @@
import { computed } from 'vue';
import { fileGetValue, fileSetValue, useJVxeUploadCell } from '/@/components/jeecg/JVxeTable/src/hooks/cells/useJVxeUploadCell';
import { uploadUrl } from '/@/api/common/api';
import { JUploadModal, UploadTypeEnum } from '/@/components/Form/src/jeecg/components/JUpload';
import { useModal } from '/@/components/Modal';
import { JVxeComponent } from '/@/components/jeecg/JVxeTable/src/types/JVxeComponent';
import { Icon } from '/@/components/Icon';
import { Dropdown } from 'ant-design-vue';
import { LoadingOutlined } from '@ant-design/icons-vue';
export function useFileCell(props, fileType: UploadTypeEnum, options?) {
const setup = useJVxeUploadCell(props, { token: true, action: uploadUrl, ...options });
const { innerFile, handleChangeCommon, originColumn } = setup;
const [registerModel, { openModal }] = useModal();
// 截取文件名
const ellipsisFileName = computed(() => {
let length = 5;
let file = innerFile.value;
if (!file || !file.name) {
return '';
}
if (file.name.length > length) {
return file.name.substr(0, length) + '…';
}
return file.name;
});
const modalValue = computed(() => {
if (innerFile.value) {
if (innerFile.value['url']) {
return innerFile.value['url'];
} else if (innerFile.value['path']) {
return innerFile.value['path'];
}
}
return '';
});
const maxCount = computed(() => {
let maxCount = originColumn.value.maxCount;
// online 扩展JSON
if (originColumn.value && originColumn.value.fieldExtendJson) {
let json = JSON.parse(originColumn.value.fieldExtendJson);
maxCount = json.uploadnum ? json.uploadnum : 0;
}
return maxCount ?? 0;
});
// 点击更多按钮
function handleMoreOperation() {
openModal(true, {
removeConfirm: true,
mover: true,
download: true,
...originColumn.value.props,
maxCount: maxCount.value,
fileType: fileType,
});
}
// 更多上传回调
function onModalChange(path) {
if (path) {
// update-begin--author:liaozhiyang---date:20240524---for【TV360X-235】富文本禁用状态下图片上传按钮文字看不清
if (innerFile.value === null) {
innerFile.value = {};
}
// update-end-author:liaozhiyang---date:20240524---for【TV360X-235】富文本禁用状态下图片上传按钮文字看不清
innerFile.value.path = path;
handleChangeCommon(innerFile.value);
} else {
//update-begin-author:liusq date:2023-06-05 for: [issues/530]JVxeTable 的JVxeTypes.image类型无法全部删除上传图片
handleChangeCommon(null);
//update-end-author:liusq date:2023-06-05 for: [issues/530]JVxeTable 的JVxeTypes.image类型无法全部删除上传图片
}
}
return {
...setup,
modalValue,
maxCount,
ellipsisFileName,
registerModel,
onModalChange,
handleMoreOperation,
};
}
export const components = {
Icon,
Dropdown,
LoadingOutlined,
JUploadModal,
};
export const enhanced = {
switches: { visible: true },
getValue: (value) => fileGetValue(value),
setValue: (value) => fileSetValue(value),
} as JVxeComponent.EnhancedPartial;