3.7.1版本发布

This commit is contained in:
JEECG
2024-09-10 15:40:34 +08:00
parent 17c68f6d53
commit 13cb18b707
106 changed files with 2832 additions and 1721 deletions

View File

@ -138,7 +138,9 @@
.item-style() {
li {
display: inline-block;
width: 100%;
//update-begin---author:wangshuai---date:2024-06-24---for:【TV360X-1576】右键样式选中缺少了一块---
width: 100% !important;
//update-end---author:wangshuai---date:2024-06-24---for:【TV360X-1576】右键样式选中缺少了一块---
height: @default-height;
margin: 0 !important;
line-height: @default-height;

View File

@ -10,6 +10,7 @@
:formProps="getProps"
:allDefaultValues="defaultValueRef"
:formModel="formModel"
:formName="getBindValue.name"
:setFormModel="setFormModel"
:validateFields="validateFields"
:clearValidate="clearValidate"
@ -213,6 +214,7 @@
getFieldsValue,
updateSchema,
resetSchema,
getSchemaByField,
appendSchemaByField,
removeSchemaByFiled,
resetFields,
@ -308,6 +310,7 @@
resetSchema,
setProps,
getProps,
getSchemaByField,
removeSchemaByFiled,
appendSchemaByField,
clearValidate,

View File

@ -1,6 +1,6 @@
<template>
<a-col v-bind="actionColOpt" v-if="showActionButtonGroup">
<div style="width: 100%" :style="{ textAlign: actionColOpt.style.textAlign }">
<div class="btnArea" style="width: 100%" :style="{ textAlign: actionColOpt.style.textAlign }">
<FormItem>
<!-- update-begin-author:zyf Date:20211213 for调换按钮前后位置-->
<slot name="submitBefore"></slot>
@ -126,3 +126,16 @@
},
});
</script>
<style lang="less" scoped>
// update-begin--author:liaozhiyang---date:20240617---for【TV360X-999】在1753px宽度下 流程设计页面查询的展开换行了
.btnArea {
:deep(.ant-form-item-control-input-content) {
display: flex;
align-items: center;
.ant-btn-link {
padding-left: 0;
}
}
}
// update-end--author:liaozhiyang---date:20240617---for【TV360X-999】在1753px宽度下 流程设计页面查询的展开换行了
</style>

View File

@ -18,6 +18,8 @@
import { useAppInject } from '/@/hooks/web/useAppInject';
import { usePermission } from '/@/hooks/web/usePermission';
import Middleware from './Middleware.vue';
import { useLocaleStoreWithOut } from '/@/store/modules/locale';
export default defineComponent({
name: 'BasicFormItem',
inheritAttrs: false,
@ -58,10 +60,16 @@
default: null,
},
// update-end-author:liaozhiyang---date:20240605---for【TV360X-857】解决禁用状态下触发校验
// update-begin--author:liaozhiyang---date:20240625---for【TV360X-1511】blur不生效
formName: {
type: String,
default: '',
},
// update-end--author:liaozhiyang---date:20240625---for【TV360X-1511】blur不生效
},
setup(props, { slots }) {
const { t } = useI18n();
const localeStore = useLocaleStoreWithOut();
const { schema, formProps } = toRefs(props) as {
schema: Ref<FormSchema>;
formProps: Ref<FormProps>;
@ -306,7 +314,7 @@
}
function renderComponent() {
const { renderComponentContent, component, field, changeEvent = 'change', valueField, componentProps, dynamicRules } = props.schema;
const { renderComponentContent, component, field, changeEvent = 'change', valueField, componentProps, dynamicRules, rules:defRules = [] } = props.schema;
const isCheck = component && ['Switch', 'Checkbox'].includes(component);
// update-begin--author:liaozhiyang---date:20231013---for【QQYUN-6679】input去空格
@ -316,6 +324,10 @@
}
// update-end--author:liaozhiyang---date:20231013---for【QQYUN-6679】input去空格
const eventKey = `on${upperFirst(changeEvent)}`;
const getRules = (): ValidationRule[] => {
const dyRules = isFunction(dynamicRules) ? dynamicRules(unref(getValues)) : [];
return [...dyRules, ...defRules];
};
// update-begin--author:liaozhiyang---date:20230922---for【issues/752】表单校验dynamicRules 无法 使用失去焦点后校验 trigger: 'blur'
const on = {
[eventKey]: (...args: Nullable<Recordable>[]) => {
@ -337,9 +349,14 @@
}
// update-end--author:liaozhiyang---date:20231013---for【QQYUN-6679】input去空格
props.setFormModel(field, value);
// update-begin--author:liaozhiyang---date:20240522---for【TV360X-341】有值之后必填校验不消失
props.validateFields([field]).catch((_) => {});
// update-end--author:liaozhiyang---date:20240522--for【TV360X-341】有值之后必填校验不消失
// update-begin--author:liaozhiyang---date:20240625---for【TV360X-1511】blur不生效
const findItem = getRules().find((item) => item?.trigger === 'blur');
if (!findItem) {
// update-begin--author:liaozhiyang---date:20240522---for【TV360X-341】有值之后必填校验不消失
props.validateFields([field]).catch((_) => {});
// update-end--author:liaozhiyang---date:20240625---for【TV360X-341】有值之后必填校验不消失
}
// update-end--author:liaozhiyang---date:20240625---for【TV360X-1511】blur不生效
},
// onBlur: () => {
// props.validateFields([field], { triggerName: 'blur' }).catch((_) => {});
@ -366,11 +383,21 @@
}
// update-end--author:liaozhiyang---date:20240308---for【QQYUN-8377】formSchema props支持动态修改
const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder;
// update-begin--author:sunjianlei---date:20240725---for【TV360X-972】控件禁用时统一占位内容
// const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder;
const isCreatePlaceholder = !!autoSetPlaceHolder;
// update-end----author:sunjianlei---date:20240725---for【TV360X-972】控件禁用时统一占位内容
// RangePicker place是一个数组
if (isCreatePlaceholder && component !== 'RangePicker' && component) {
//自动设置placeholder
propsData.placeholder = unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component) + props.schema.label;
// update-begin--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
let label = isFunction(props.schema.label) ? props.schema.label() : props.schema.label;
if (localeStore.getLocale === 'en' && !(/^\s/.test(label))) {
label = ' ' + label;
}
// update-end--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
propsData.placeholder = unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component) + label;
}
propsData.codeField = field;
propsData.formValues = unref(getValues);
@ -403,7 +430,10 @@
function renderLabelHelpMessage() {
//update-begin-author:taoyan date:2022-9-7 for: VUEN-2061【样式】online表单超出4个 .. 省略显示
//label宽度支持自定义
const { label, helpMessage, helpComponentProps, subLabel, labelLength } = props.schema;
const { label: itemLabel, helpMessage, helpComponentProps, subLabel, labelLength } = props.schema;
// update-begin--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
const label = isFunction(itemLabel) ? itemLabel() : itemLabel;
// update-end--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
let showLabel: string = label + '';
// update-begin--author:liaozhiyang---date:20240517---for【TV360X-98】label展示的文字必须和labelLength配置一致
if (labelLength) {
@ -469,7 +499,7 @@
<div style="display:flex">
{/* author: sunjianlei for: 【VUEN-744】此处加上 width: 100%; 因为要防止组件宽度超出 FormItem */}
{/* update-begin--author:liaozhiyang---date:20240510---for【TV360X-719】表单校验不通过项滚动到可视区内 */}
<Middleware>{getContent()}</Middleware>
<Middleware formName={props.formName} fieldName={field}>{getContent()}</Middleware>
{/* update-end--author:liaozhiyang---date:20240510---for【TV360X-719】表单校验不通过项滚动到可视区内 */}
{showSuffix && <span class="suffix">{getSuffix}</span>}
</div>

View File

@ -5,12 +5,20 @@
</template>
<script setup>
import { Form } from 'ant-design-vue';
import { computed } from 'vue';
const formItemContext = Form.useInjectFormItemContext();
const formItemId = computed(() => {
return formItemContext.id.value;
});
import { ref } from 'vue';
// update-begin--author:liaozhiyang---date:20240625---for【TV360X-1511】blur不生效
const formItemId = ref(null);
const props = defineProps(['formName', 'fieldName']);
if (props.formName && props.fieldName) {
formItemId.value = `${props.formName}_${props.fieldName}`;
}
// update-end--author:liaozhiyang---date:20240625---for【TV360X-1511】blur不生效
</script>
<style lang="scss" scoped></style>
<style lang="less" scoped>
// update-begin--author:liaozhiyang---date:20240617---for【TV360X-1253】代码生成查询区域和新增组件没撑满
div > :deep(.ant-picker) {
width: 100%;
}
// update-end--author:liaozhiyang---date:20240617---for【TV360X-1253】代码生成查询区域和新增组件没撑满
</style>

View File

@ -1,5 +1,5 @@
<template>
<div :class="formDisabled ? 'jeecg-form-container-disabled jeecg-form-detail-effect' : ''">
<div :class="formDisabled ? 'jeecg-form-container-disabled jeecg-form-detail-effect' : 'jeecg-and-modal-form'">
<fieldset :disabled="formDisabled">
<slot name="detail"></slot>
</fieldset>
@ -39,6 +39,23 @@
</script>
<style scoped lang="less">
// update-begin--author:liaozhiyang---date:20240719---for【TV360X-1090】表单label超长省略显示
.jeecg-and-modal-form {
:deep(.ant-form-item-label) {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-right: 6px;
> label {
line-height: 32px;
display: inline;
&::after {
margin-right: 0;
}
}
}
}
// update-end--author:liaozhiyang---date:20240719---for【TV360X-1090】表单label超长省略显示
.jeecg-form-container-disabled {
cursor: not-allowed;
}

View File

@ -5,7 +5,7 @@ import { handleRangeValue } from '../utils/formUtils';
import { ref, onUnmounted, unref, nextTick, watch } from 'vue';
import { isProdMode } from '/@/utils/env';
import { error } from '/@/utils/log';
import { getDynamicProps, getValueType } from '/@/utils';
import { getDynamicProps, getValueType, getValueTypeBySchema } from '/@/utils';
import { add } from "/@/components/Form/src/componentMap";
//集成online专用控件
import { OnlineSelectCascade, LinkTableCard, LinkTableSelect } from '@jeecg/online';
@ -137,7 +137,7 @@ export function useForm(props?: Props): UseFormReturnType {
let values = form.validate(nameList).then((values) => {
for (let key in values) {
if (values[key] instanceof Array) {
let valueType = getValueType(getProps, key);
let valueType = getValueTypeBySchema(form.getSchemaByField(key)!);
if (valueType === 'string') {
values[key] = values[key].join(',');
}

View File

@ -94,6 +94,23 @@ export function useFormEvents({
});
validateFields(validKeys).catch((_) => {});
}
/**
* 根据字段名获取schema
* @param field
*/
function getSchemaByField(field: string): Nullable<FormSchema> {
if (!isString(field)) {
return null
}
const schemaList: FormSchema[] = unref(getSchema);
const index = schemaList.findIndex((schema) => schema.field === field);
if (index !== -1) {
return cloneDeep(schemaList[index]);
}
return null
}
/**
* @description: Delete based on field name
*/
@ -270,6 +287,7 @@ export function useFormEvents({
getFieldsValue,
updateSchema,
resetSchema,
getSchemaByField,
appendSchemaByField,
removeSchemaByFiled,
resetFields,

View File

@ -22,7 +22,7 @@
showArea: propTypes.bool.def(true),
//是否是全部
showAll: propTypes.bool.def(false),
// 存储数据
// 存储数据 all时传递到外面的是数组province, city, region传递外面的是字符串
saveCode: propTypes.oneOf(['province', 'city', 'region', 'all']).def('all'),
},
emits: ['options-change', 'change', 'update:value'],
@ -97,6 +97,25 @@
}
return result;
}
/**
* liaozhiyang
* 2024-06-17
* 【TV360X-1224】省市区组件默认传到外面的值是字符串逗号分隔
* */
const send = (data) => {
let result = data;
if (result) {
if (props.saveCode === 'all') {
// 传递的是数组
} else {
// 传递的是字符串
result = data.join(',');
}
}
emit('change', result);
emit('update:value', result);
};
function handleChange(arr, ...args) {
// update-begin--author:liaozhiyang---date:20240607---for【TV360X-501】省市区换新组件
if (arr?.length) {
@ -111,11 +130,9 @@
} else {
result = arr;
}
emit('change', result);
emit('update:value', result);
send(result);
} else {
emit('change', arr);
emit('update:value', arr);
send(arr);
}
// update-end--author:liaozhiyang---date:20240607---for【TV360X-501】省市区换新组件
// emitData.value = args;
@ -124,6 +141,7 @@
// state.value = result;
//update-end-author:taoyan date:2022-6-27 for: VUEN-1424【vue3】树表、单表、jvxe、erp 、内嵌子表省市县 选择不上
}
return {
cascaderValue,
attrs,

View File

@ -84,7 +84,9 @@
() => {
loadItemByCode();
},
{ deep: true }
//update-begin---author:wangshuai---date:2024-06-17---for:【TV360X-480】封装表单和原生表单默认值生成有问题的字段分类字典树附默认值不生效---
{ deep: true, immediate: true }
//update-end---author:wangshuai---date:2024-06-17---for:【TV360X-480】封装表单和原生表单默认值生成有问题的字段分类字典树附默认值不生效---
);
watch(
() => props.pcode,
@ -124,6 +126,7 @@
treeValue.value = { value: null, label: null };
}
} else {
console.log("props.value:::",props.value)
loadDictItem({ ids: props.value }).then((res) => {
let values = props.value.split(',');
treeValue.value = res.map((item, index) => ({

View File

@ -10,7 +10,7 @@
import { defineComponent, computed, watch, watchEffect, ref, unref } from 'vue';
import { propTypes } from '/@/utils/propTypes';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { initDictOptions } from '/@/utils/dict/index';
import {getDictItems} from "@/api/common/api";
export default defineComponent({
name: 'JCheckbox',
@ -67,21 +67,30 @@
}
//根据字典Code, 初始化选项
if (props.dictCode) {
const dictData = await initDictOptions(props.dictCode);
checkOptions.value = dictData.reduce((prev, next) => {
if (next) {
const value = next['value'];
prev.push({
label: next['text'],
value: value,
color: next['color'],
});
}
return prev;
}, []);
loadDictOptions()
}
}
// 根据字典code查询字典项
function loadDictOptions() {
//update-begin-author:taoyan date:2022-6-21 for: 字典数据请求前将参数编码处理,但是不能直接编码,因为可能之前已经编码过了
let temp = props.dictCode || '';
if (temp.indexOf(',') > 0 && temp.indexOf(' ') > 0) {
// 编码后 是不包含空格的
temp = encodeURI(temp);
}
//update-end-author:taoyan date:2022-6-21 for: 字典数据请求前将参数编码处理,但是不能直接编码,因为可能之前已经编码过了
getDictItems(temp).then((res) => {
if (res) {
checkOptions.value = res.map((item) => ({value: item.value, label: item.text, color: item.color}));
//console.info('res', dictOptions.value);
} else {
console.error('getDictItems error: : ', res);
checkOptions.value = [];
}
});
}
/**
* change事件
* @param $event

View File

@ -44,6 +44,9 @@
import 'codemirror/addon/hint/anyword-hint.js';
// 匹配括号
import 'codemirror/addon/edit/matchbrackets';
// 占位符
import 'codemirror/addon/display/placeholder.js';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { useDesign } from '/@/hooks/web/useDesign';
import { isJsonObjectString } from '/@/utils/is.ts';
@ -356,6 +359,10 @@
.CodeMirror{
border: 1px solid #ddd;
}
.CodeMirror pre.CodeMirror-placeholder {
color: #cacaca;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;
}
}
.CodeMirror-hints.idea,
.CodeMirror-hints.monokai {

View File

@ -17,6 +17,8 @@
props: {
value: propTypes.string.def(''),
disabled: propTypes.bool.def(false),
//是否聚焦
autoFocus: propTypes.bool.def(true),
},
emits: ['change', 'update:value'],
setup(props, { emit, attrs }) {

View File

@ -183,6 +183,16 @@
if (file.status === 'error') {
createMessage.error(`${file.name} 上传失败.`);
}
// update-begin--author:liaozhiyang---date:20240704---for【TV360X-1640】上传图片大小超出限制显示优化
if (file.status === 'done' && file.response.success === false) {
const failIndex = uploadFileList.value.findIndex((item) => item.uid === file.uid);
if (failIndex != -1) {
uploadFileList.value.splice(failIndex, 1);
}
createMessage.warning(file.response.message);
return;
}
// update-end--author:liaozhiyang---date:20240704---for【TV360X-1640】上传图片大小超出限制显示优化
let fileUrls = [];
let noUploadingFileCount = 0;
if (file.status != 'uploading') {

View File

@ -17,6 +17,7 @@
v-if="selectLocation === 'right'"
v-model:value="selectVal"
@change="handleSelectChange"
:style="{width:props.selectWidth}"
>
<a-select-option v-for="item in options" :key="item.value">{{ item.label }}</a-select-option>
</a-select>
@ -33,6 +34,7 @@
selectLocation: propTypes.oneOf(['left', 'right']).def('right'),
selectPlaceholder: propTypes.string.def(''),
inputPlaceholder: propTypes.string.def(''),
selectWidth:propTypes.string.def('auto'),
});
const emit = defineEmits(['update:value', 'change']);
const selectVal = ref<string>();

View File

@ -23,8 +23,9 @@
:groupId="uniqGroupId"
:param="param"
:showAdvancedButton="showAdvancedButton"
@ok="callBack"
:getContainer="getContainer"
:getFormValues="getFormValues"
@ok="callBack"
></JPopupOnlReportModal>
</a-form-item>
<!-- update-end--author:liaozhiyang---date:20240515---forQQYUN-9260必填模式下会影响到弹窗内antd组件的样式 -->
@ -56,6 +57,7 @@
groupId: propTypes.string.def(''),
formElRef: propTypes.object,
setFieldsValue: propTypes.func,
getFormValues: propTypes.func,
getContainer: propTypes.func,
fieldConfig: {
type: Array,

View File

@ -15,9 +15,10 @@
:sorter="sorter"
:groupId="''"
:param="param"
@ok="callBack"
:getFormValues="getFormValues"
:getContainer="getContainer"
:showAdvancedButton="showAdvancedButton"
@ok="callBack"
/>
</a-form-item>
<!-- update-end--author:liaozhiyang---date:20240515---forQQYUN-9260必填模式下会影响到弹窗内antd组件的样式 -->
@ -56,6 +57,7 @@
multi: propTypes.bool.def(false),
param: propTypes.object.def({}),
spliter: propTypes.string.def(','),
getFormValues: propTypes.func,
getContainer: propTypes.func,
showAdvancedButton: propTypes.bool.def(true),
},

View File

@ -39,8 +39,10 @@
function emitArray() {
let arr = [];
let begin = beginValue.value || '';
let end = endValue.value || '';
// update-begin--author:liaozhiyang---date:20240704---for【TV360X-1749】数量0输入不了输入清空了
let begin = beginValue.value ?? '';
let end = endValue.value ?? '';
// update-end--author:liaozhiyang---date:20240704---for【TV360X-1749】数量0输入不了输入清空了
arr.push(begin);
arr.push(end);
emit('change', arr);

View File

@ -14,11 +14,12 @@
@focus="handleAsyncFocus"
@search="loadData"
@change="handleAsyncChange"
@popupScroll="handlePopupScroll"
>
<template #notFoundContent>
<a-spin size="small" />
</template>
<a-select-option v-for="d in options" :key="d.value" :value="d.value">{{ d.text }}</a-select-option>
<a-select-option v-for="d in options" :key="d?.value" :value="d?.value">{{ d?.text }}</a-select-option>
</a-select>
<!--字典下拉搜素-->
<a-select
@ -36,7 +37,7 @@
<template #notFoundContent>
<a-spin v-if="loading" size="small" />
</template>
<a-select-option v-for="d in options" :key="d.value" :value="d.value">{{ d.text }}</a-select-option>
<a-select-option v-for="d in options" :key="d?.value" :value="d?.value">{{ d?.text }}</a-select-option>
</a-select>
</template>
@ -95,6 +96,11 @@
const lastLoad = ref(0);
// 是否根据value加载text
const loadSelectText = ref(true);
// 异步(字典表) - 滚动加载时会用到
let isHasData = true;
let scrollLoading = false;
let pageNo = 1;
let searchKeyword = '';
// 是否是字典表
const isDictTable = computed(() => {
@ -152,6 +158,12 @@
if (!isDictTable.value) {
return;
}
// update-begin--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
pageNo = 1;
isHasData = true;
searchKeyword = value;
// update-end--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
lastLoad.value += 1;
const currentLoad = unref(lastLoad);
options.value = [];
@ -164,7 +176,7 @@
defHttp
.get({
url: `/sys/dict/loadDict/${props.dict}`,
params: { keyword: keywordInfo, pageSize: props.pageSize },
params: { keyword: keywordInfo, pageSize: props.pageSize, pageNo },
})
.then((res) => {
loading.value = false;
@ -173,6 +185,13 @@
return;
}
options.value = res;
// update-begin--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
pageNo++;
// update-end--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
} else {
// update-begin--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
pageNo == 1 && (isHasData = false);
// update-end--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
}
});
}, 300);
@ -195,10 +214,12 @@
key: value,
label: res,
};
selectedAsyncValue.value = { ...obj };
if (props.value == value) {
selectedAsyncValue.value = { ...obj };
}
//update-begin-author:taoyan date:2022-8-11 for: 值改变触发change事件--用于online关联记录配置页面
if(props.immediateChange == true){
emit('change', value);
emit('change', props.value);
}
//update-end-author:taoyan date:2022-8-11 for: 值改变触发change事件--用于online关联记录配置页面
}
@ -243,18 +264,31 @@
if (!dict) {
console.error('搜索组件未配置字典项');
} else {
// update-begin--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
pageNo = 1;
isHasData = true;
searchKeyword = '';
// update-end--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
//异步一开始也加载一点数据
loading.value = true;
let keywordInfo = getKeywordParam('');
defHttp
.get({
url: `/sys/dict/loadDict/${dict}`,
params: { pageSize: pageSize, keyword: keywordInfo },
params: { pageSize: pageSize, keyword: keywordInfo, pageNo },
})
.then((res) => {
loading.value = false;
if (res && res.length > 0) {
options.value = res;
// update-begin--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
pageNo++;
// update-end--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
} else {
// update-begin--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
pageNo == 1 && (isHasData = false);
// update-end--author:liaozhiyang---date:20240731---for【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
}
});
}
@ -355,11 +389,57 @@
// update-begin--author:liaozhiyang---date:20240523---for【TV360X-26】下拉搜索控件选中选项后再次点击下拉应该显示初始的下拉选项而不是只展示选中结果
const handleAsyncFocus = () => {
// update-begin--author:liaozhiyang---date:20240709---for【issues/6681】异步查询不生效
(isObject(selectedAsyncValue.value) || selectedAsyncValue.value?.length) && isDictTable.value && props.async && initDictTableData();
if ((isObject(selectedAsyncValue.value) || selectedAsyncValue.value?.length) && isDictTable.value && props.async) {
// update-begin--author:liaozhiyang---date:20240809---for【TV360X-2062】下拉搜索选择第二页数据后第一次点击时(得到焦点)滚动条没复原到初始位置且数据会加载第二页数据(应该只加载第一页数据)
options.value = [];
// update-end--author:liaozhiyang---date:20240809---for【TV360X-2062】下拉搜索选择第二页数据后第一次点击时(得到焦点)滚动条没复原到初始位置且数据会加载第二页数据(应该只加载第一页数据)
initDictTableData();
}
// update-end--author:liaozhiyang---date:20240709---for【issues/6681】异步查询不生效
attrs.onFocus?.();
};
// update-end--author:liaozhiyang---date:20240523---for【TV360X-26】下拉搜索控件选中选项后再次点击下拉应该显示初始的下拉选项而不是只展示选中结果
/**
* 2024-07-30
* liaozhiyang
* 【TV360X-1898】JsearchSelect组件传入字典表格式则支持滚动加载
* */
const handlePopupScroll = async (e) => {
// 字典表才才支持滚动加载
if (isDictTable.value) {
const { target } = e;
const { scrollTop, scrollHeight, clientHeight } = target;
if (!scrollLoading && isHasData && scrollTop + clientHeight >= scrollHeight - 10) {
scrollLoading = true;
let keywordInfo = getKeywordParam(searchKeyword);
defHttp
.get({ url: `/sys/dict/loadDict/${props.dict}`, params: { pageSize: props.pageSize, keyword: keywordInfo, pageNo } })
.then((res) => {
loading.value = false;
if (res?.length > 0) {
// 防止开源只更新了前端代码没更新后端代码(第一页和第二页面的第一条数据相同则是后端代码没更新,没分页)
if (JSON.stringify(res[0]) === JSON.stringify(options.value[0])) {
isHasData = false;
return;
}
options.value.push(...res);
pageNo++;
} else {
isHasData = false;
}
})
.finally(() => {
scrollLoading = false;
})
.catch(() => {
pageNo != 1 && pageNo--;
});
}
}
};
return {
attrs,
options,
@ -373,6 +453,7 @@
handleChange,
handleAsyncChange,
handleAsyncFocus,
handlePopupScroll,
};
},
});

View File

@ -152,6 +152,11 @@
let result = typeof props.value == 'string' ? values.join(',') : values;
emit('update:value', result);
emit('change', result);
// update-begin--author:liaozhiyang---date:20240627---for【TV360X-1648】用户编辑界面“所属部门”与“负责部门”联动出错同步之前丢的代码
if (!values || values.length == 0) {
emit('select', null, null);
}
// update-end--author:liaozhiyang---date:20240627---for【TV360X-1648】用户编辑界面“所属部门”与“负责部门”联动出错同步之前丢的代码
};
// update-end--author:liaozhiyang---date:20240527---for【TV360X-414】部门设置了默认值查询重置变成空了(同步JSelectUser组件改法)

View File

@ -91,6 +91,17 @@
}
});
watch(
() => props.dictCode,
() => {
if (props.dictCode) {
loadDictOptions();
} else {
dictOptions.value = props.options;
}
}
);
watch(
() => props.value,
(val) => {

View File

@ -305,7 +305,10 @@
} else if (info.file.status === 'error') {
createMessage.error(`${info.file.name} 上传失败.`);
}
fileList.value = fileListTemp;
// update-begin--author:liaozhiyang---date:20240628---for【issues/1273】上传组件JUpload配置beforeUpload阻止了上传前端页面中还是显示缩略图
// beforeUpload 返回false则没有status
info.file.status && (fileList.value = fileListTemp);
// update-end--author:liaozhiyang---date:20240628---for【issues/1273】上传组件JUpload配置beforeUpload阻止了上传前端页面中还是显示缩略图
if (info.file.status === 'done' || info.file.status === 'removed') {
//returnUrl为true时仅返回文件路径
if (props.returnUrl) {

View File

@ -1,6 +1,9 @@
<template>
<div>
<a-row class="j-select-row" type="flex" :gutter="8">
<div v-if="isDetailsMode">
<p class="detailStr" :title="detailStr">{{ detailStr }}</p>
</div>
<a-row v-else class="j-select-row" type="flex" :gutter="8">
<a-col class="left" :class="{ full: !showButton }">
<!-- 显示加载效果 -->
<a-input v-if="loading" readOnly placeholder="加载中…">
@ -32,7 +35,7 @@
</div>
</template>
<script lang="ts">
import { defineComponent, ref, inject, reactive } from 'vue';
import { defineComponent, ref, inject, reactive, watch } from 'vue';
import { propTypes } from '/@/utils/propTypes';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { LoadingOutlined } from '@ant-design/icons-vue';
@ -59,6 +62,8 @@
maxTagCount: propTypes.number,
// buttonIcon
buttonIcon: propTypes.string.def(''),
// 【TV360X-1002】是否是详情模式
isDetailsMode: propTypes.bool.def(false),
},
emits: ['handleOpen', 'change'],
setup(props, { emit, refs }) {
@ -67,7 +72,7 @@
//接收选择的值
const selectValues = inject('selectValues') || ref({});
const attrs = useAttrs();
const detailStr = ref('');
/**
* 打开弹出框
*/
@ -89,12 +94,28 @@
emit('change', value);
}
// -update-begin--author:liaozhiyang---date:20240617---for【TV360X-1002】详情页面行编辑用户组件和部门组件显示方式优化
watch(
[selectValues, options],
() => {
if (props.isDetailsMode) {
if (Array.isArray(selectValues.value) && Array.isArray(options.value)) {
const result = options.value.map((item) => item.label);
detailStr.value = result.join(',');
}
}
},
{ immediate: true }
);
// -update-end--author:liaozhiyang---date:20240617---for【TV360X-1002】详情页面行编辑用户组件和部门组件显示方式优化
return {
attrs,
selectValues,
options,
handleChange,
openModal,
detailStr,
};
},
});
@ -119,4 +140,10 @@
display: none !important;
}
}
.detailStr {
margin: 0;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -80,7 +80,7 @@
const queryUrl = getQueryUrl();
const [{ visibleChange, checkedKeys, getCheckStrictly, getSelectTreeData, onCheck, onLoadData, treeData, checkALL, expandAll, onSelect }] =
useTreeBiz(treeRef, queryUrl, getBindValue, props);
useTreeBiz(treeRef, queryUrl, getBindValue, props, emit);
const searchInfo = ref(props.params);
const tree = ref([]);
//替换treeNode中key字段为treeData中对应的字段

View File

@ -12,8 +12,8 @@
wrapClassName="j-popup-modal"
@visible-change="visibleChange"
>
<div class="jeecg-basic-table-form-container" v-if="showSearchFlag">
<a-form ref="formRef" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol" @keyup.enter.native="searchQuery">
<div class="jeecg-basic-table-form-container">
<a-form ref="formRef" v-if="showSearchFlag" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<template v-for="(item, index) in queryInfo">
<template v-if="item.hidden === '1'">
@ -31,8 +31,8 @@
<a-col :md="8" :sm="8" v-if="showAdvancedButton">
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
<a-col :lg="6">
<a-button type="primary" preIcon="ant-design:reload-outlined" @click="searchReset">重置</a-button>
<a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery" style="margin-left: 8px">查询</a-button>
<a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button>
<a-button preIcon="ant-design:reload-outlined" @click="searchReset" style="margin-left: 8px">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<Icon :icon="toggleSearchStatus ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
@ -59,6 +59,12 @@
@change="handleChangeInTable"
>
<template #tableTitle></template>
<template #bodyCell="{text, column}">
<template v-if="column.fieldType === 'Image'">
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
<img v-else :src="getImgView(text)" alt="图片不存在" class="cellIamge" @click="viewOnlineCellImage($event, text)" />
</template>
</template>
</BasicTable>
</BasicModal>
</div>
@ -71,6 +77,8 @@
import { useAttrs } from '/@/hooks/core/useAttrs';
import { usePopBiz } from '/@/components/jeecg/OnLine/hooks/usePopBiz';
import { useMessage } from '/@/hooks/web/useMessage';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { createImgPreview } from '/@/components/Preview/index';
export default defineComponent({
name: 'JPopupOnlReportModal',
@ -82,9 +90,9 @@
loading: true,
}),
},
props: ['multi', 'code', 'sorter', 'groupId', 'param','showAdvancedButton'],
props: ['multi', 'code', 'sorter', 'groupId', 'param','showAdvancedButton', 'getFormValues'],
emits: ['ok', 'register'],
setup(props, { emit, refs }) {
setup(props, { emit }) {
const { createMessage } = useMessage();
const labelCol = reactive({
xs: { span: 24 },
@ -242,6 +250,41 @@
queryParam.value = {};
loadData(1);
}
/**
* 2024-07-24
* liaozhiyang
* 【TV360X-1756】报表添加图片类型
* 图片
* @param text
*/
function getImgView(text) {
if (text && text.indexOf(',') > 0) {
text = text.substring(0, text.indexOf(','));
}
return getFileAccessHttpUrl(text);
}
/**
* 2024-07-24
* liaozhiyang
* 【TV360X-1756】报表添加图片类型
* 预览列表 cell 图片
* @param text
*/
function viewOnlineCellImage(e, text) {
e.stopPropagation();
if (text) {
let imgList: any = [];
let arr = text.split(',');
for (let str of arr) {
if (str) {
imgList.push(getFileAccessHttpUrl(str));
}
}
createImgPreview({ imageList: imgList });
}
}
return {
attrs,
register,
@ -272,6 +315,8 @@
handleToggleSearch,
searchQuery,
searchReset,
getImgView,
viewOnlineCellImage,
};
},
});
@ -290,4 +335,12 @@
:deep(.jeecg-basic-table .ant-table-wrapper .ant-table-title){
min-height: 0;
}
.cellIamge {
height: 25px !important;
margin: 0 auto;
max-width: 80px;
font-size: 12px;
font-style: italic;
cursor: pointer;
}
</style>

View File

@ -29,6 +29,7 @@
:rowSelection="rowSelection"
:indexColumnProps="indexColumnProps"
:afterFetch="afterFetch"
:beforeFetch="beforeFetch"
>
<!-- update-begin-author:taoyan date:2022-5-25 for: VUEN-1112一对多 用户选择 未显示选择条数及清空 -->
<template #tableTitle></template>
@ -267,6 +268,17 @@
maxHeight.value = clientHeight > 600 ? 600 : clientHeight;
// update-end--author:liaozhiyang---date:20240607---for【TV360X-305】小屏幕展示10条
//update-begin---author:wangshuai---date:2024-07-03---for:【TV360X-1629】用户选择组件不是根据创建时间正序排序的---
/**
* 请求之前根据创建时间排序
*
* @param params
*/
function beforeFetch(params) {
return Object.assign({ column: 'createTime', order: 'desc' }, params);
}
//update-end---author:wangshuai---date:2024-07-03---for:【TV360X-1629】用户选择组件不是根据创建时间正序排序的---
return {
//config,
handleOk,
@ -287,6 +299,7 @@
afterFetch,
handleCancel,
maxHeight,
beforeFetch,
};
},
});

View File

@ -39,12 +39,22 @@ export const useCodeHinting = (CodeMirror, keywords, language) => {
// 查找.前面是否有定义的关键词
const curLineCode = cm.getLine(cur.line);
for (let i = 0, len = customKeywords.length; i < len; i++) {
const k = curLineCode.substring(-1, customKeywords[i].length);
const k = curLineCode.slice(-(customKeywords[i].length + 1), -1);
if (customKeywords.includes(k)) {
recordKeyword = k;
break;
}
}
} else {
// 查找单词前面是否有.this(.关键词)
const curLineCode = cm.getLine(cur.line);
for (let i = 0, len = customKeywords.length; i < len; i++) {
const k = curLineCode.slice(start - (customKeywords[i].length + 1), start);
if (k.substr(-1) === '.' && customKeywords.includes(k.replace('.', ''))) {
recordKeyword = k.replace('.', '');
break;
}
}
}
const findIdx = (a, b) => a.toLowerCase().indexOf(b.toLowerCase());
let list = currentKeywords.filter((item) => {

View File

@ -2,7 +2,7 @@ import { inject, reactive, ref, watch, unref, Ref } from 'vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { isEmpty } from '@/utils/is';
export function useSelectBiz(getList, props, emit) {
export function useSelectBiz(getList, props, emit?) {
//接收下拉框选项
const selectOptions = inject('selectOptions', ref<Array<object>>([]));
//接收已选择的值
@ -120,7 +120,7 @@ export function useSelectBiz(getList, props, emit) {
props.showSelected && initSelectRows();
} else {
// update-begin--author:liaozhiyang---date:20240517---for【QQYUN-9366】用户选择组件取消和关闭会把选择数据带入
emit('close');
emit?.('close');
// update-end--author:liaozhiyang---date:20240517---for【QQYUN-9366】用户选择组件取消和关闭会把选择数据带入
}
}

View File

@ -35,6 +35,7 @@ export interface FormActionType {
resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
setProps: (formProps: Partial<FormProps>) => Promise<void>;
getProps: ComputedRef<Partial<FormProps>>;
getSchemaByField: (field: string) => Nullable<FormSchema>;
removeSchemaByFiled: (field: string | string[]) => Promise<void>;
appendSchemaByField: (schema: FormSchema, prefixField: string | undefined, first?: boolean | undefined) => Promise<void>;
validateFields: (nameList?: NamePath[], options?: ValidateOptions) => Promise<any>;
@ -131,7 +132,9 @@ export interface FormSchema {
// Variable name bound to v-model Default value
valueField?: string;
// Label name
label: string | VNode;
// update-begin--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
label: string | VNode | Fn;
// update-end--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
// Auxiliary text
subLabel?: string;
// Help text on the right side of the text

View File

@ -1,4 +1,4 @@
import REGION_DATA from 'china-area-data';
import {pcaa as REGION_DATA} from "@/utils/areaData/pcaUtils";
/**
* Area 属性all的类型
@ -18,7 +18,7 @@ class Area {
/**
* 构造器
* @param express
* @param pcaa
*/
constructor(pcaa?) {
if (!pcaa) {
@ -101,6 +101,11 @@ const jeecgAreaData = new Area();
// 根据code找文本
const getAreaTextByCode = function (code) {
let index = 3;
// update-begin--author:liaozhiyang---date:20240617---for【TV360X-1210】online列表香港、澳门没翻译香港、澳门只有两级其它省份是三级
if (code && ['82', '81'].includes(code.substring(0, 2))) {
index = 2;
}
// update-end--author:liaozhiyang---date:20240617---for【TV360X-1210】online列表香港、澳门没翻译香港、澳门只有两级其它省份是三级
//update-begin-author:liusq---date:20220531--for: 判断code是否是多code逗号分割的字符串是的话获取最后一位的code ---
if (code && code.includes(',')) {
index = code.split(",").length;

View File

@ -1,4 +1,4 @@
import REGION_DATA from 'china-area-data';
import {pcaa as REGION_DATA} from "@/utils/areaData/pcaUtils";
import { cloneDeep } from 'lodash-es';
// code转汉字大对象

View File

@ -53,6 +53,7 @@
Empty,
Pagination,
},
emits: ['update:value'],
props: {
currentList: propTypes.any.def([]),
clearSelect: propTypes.bool.def(false),
@ -141,6 +142,7 @@
}
}
}
emit('update:value', currentSelect.value);
}
/**

View File

@ -11,37 +11,37 @@
<a-tabs style="padding-left: 15px;padding-right: 15px">
<a-tab-pane tab="方向性图标" key="1">
<a-form-item-rest>
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="directionIcons" :value="currentSelect" />
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="directionIcons" v-model:value="selectIcon" />
</a-form-item-rest>
</a-tab-pane>
<a-tab-pane tab="指示性图标" key="2">
<a-form-item-rest>
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="suggestionIcons" :value="currentSelect" />
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="suggestionIcons" v-model:value="selectIcon" />
</a-form-item-rest>
</a-tab-pane>
<a-tab-pane tab="编辑类图标" key="3">
<a-form-item-rest>
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="editIcons" :value="currentSelect" />
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="editIcons" v-model:value="selectIcon" />
</a-form-item-rest>
</a-tab-pane>
<a-tab-pane tab="数据类图标" key="4">
<a-form-item-rest>
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="dataIcons" :value="currentSelect" />
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="dataIcons" v-model:value="selectIcon" />
</a-form-item-rest>
</a-tab-pane>
<a-tab-pane tab="网站通用图标" key="5">
<a-form-item-rest>
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="webIcons" :value="currentSelect" />
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="webIcons" v-model:value="selectIcon" />
</a-form-item-rest>
</a-tab-pane>
<a-tab-pane tab="品牌和标识" key="6">
<a-form-item-rest>
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="logoIcons" :value="currentSelect" />
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-svg-mode="isSvgMode" :current-list="logoIcons" v-model:value="selectIcon" />
</a-form-item-rest>
</a-tab-pane>
<a-tab-pane tab="其他" key="7">
<a-form-item-rest>
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-page="true" :is-search="true" :is-svg-mode="isSvgMode" :current-list="otherIcons" :value="currentSelect" />
<icon-list ref="iconListRef" :clear-select="clearSelect" :copy="copy" :is-page="true" :is-search="true" :is-svg-mode="isSvgMode" :current-list="otherIcons" v-model:value="selectIcon" />
</a-form-item-rest>
</a-tab-pane>
</a-tabs>
@ -94,6 +94,7 @@
const isSvgMode = props.mode === 'svg';
const icons = isSvgMode ? getSvgIcons() : getIcons();
const selectIcon = ref('');
const currentSelect = ref('');
const { t } = useI18n();
@ -170,7 +171,7 @@
* 图标弹窗确定事件
*/
function handleOk() {
currentSelect.value = iconListRef.value.getIcon();
currentSelect.value = selectIcon.value;
iconOpen.value = false;
}

View File

@ -77,6 +77,9 @@
maxTagCount: 1,
// 显示提示重写,去掉省略号
maxTagPlaceholder: ({ length }) => '+' + length,
// -update-begin--author:liaozhiyang---date:20240617---for【TV360X-1002】详情页面行编辑用户组件和部门组件显示方式优化
isDetailsMode: cellProps.value.disabledTable,
// -update-end--author:liaozhiyang---date:20240617---for【TV360X-1002】详情页面行编辑用户组件和部门组件显示方式优化
};
});

View File

@ -52,6 +52,9 @@
maxTagCount: 1,
// 显示提示重写,去掉省略号
maxTagPlaceholder: ({ length }) => '+' + length,
// -update-begin--author:liaozhiyang---date:20240617---for【TV360X-1002】详情页面行编辑用户组件和部门组件显示方式优化
isDetailsMode: cellProps.value.disabledTable,
// -update-end--author:liaozhiyang---date:20240617---for【TV360X-1002】详情页面行编辑用户组件和部门组件显示方式优化
};
});

View File

@ -25,6 +25,7 @@
:dropMenuList="getDropdownList"
popconfirm
v-if="dropDownActions && getDropdownList.length > 0"
:getPopupContainer="dropdownGetPopupContainer"
>
<slot name="more"></slot>
<!-- 设置插槽 -->
@ -71,6 +72,10 @@
const { prefixCls } = useDesign('basic-table-action');
const dropdownCls = `${prefixCls}-dropdown`;
let table: Partial<TableActionType> = {};
const tempActionsAuth = {};
const tempDropdownListAuth = {};
if (!props.outside) {
table = useTableContext();
}
@ -93,7 +98,17 @@
const getActions = computed(() => {
return (toRaw(props.actions) || [])
.filter((action) => {
return hasPermission(action.auth) && isIfShow(action);
// -update-begin--author:liaozhiyang---date:20240619---for【TV360X-528】列表配置了权限在列表行上划过都会执行hasPermission
const auth: any = action.auth;
let authResult;
if (action.auth && typeof tempActionsAuth[auth] === 'boolean') {
authResult = tempActionsAuth[auth];
} else {
authResult = hasPermission(action.auth);
action.auth && (tempActionsAuth[auth] = authResult);
}
return authResult && isIfShow(action);
// -update-end--author:liaozhiyang---date:20240619---for【TV360X-528】列表配置了权限在列表行上划过都会执行hasPermission
})
.map((action) => {
const { popConfirm } = action;
@ -121,7 +136,17 @@
const getDropdownList = computed((): any[] => {
//过滤掉隐藏的dropdown,避免出现多余的分割线
const list = (toRaw(props.dropDownActions) || []).filter((action) => {
return hasPermission(action.auth) && isIfShow(action);
// -update-begin--author:liaozhiyang---date:20240619---for【TV360X-528】列表配置了权限在列表行上划过都会执行hasPermission
const auth: any = action.auth;
let authResult;
if (action.auth && typeof tempDropdownListAuth[auth] === 'boolean') {
authResult = tempDropdownListAuth[auth];
} else {
authResult = hasPermission(action.auth);
action.auth && (tempDropdownListAuth[auth] = authResult);
}
return authResult && isIfShow(action);
// -update-end--author:liaozhiyang---date:20240619---for【TV360X-528】列表配置了权限在列表行上划过都会执行hasPermission
});
return list.map((action, index) => {
const { label, popConfirm } = action;
@ -129,6 +154,13 @@
if (popConfirm) {
const overlayClassName = popConfirm.overlayClassName;
popConfirm.overlayClassName = `${overlayClassName ? overlayClassName : ''} ${prefixCls}-popconfirm`;
// update-begin--author:liaozhiyang---date:20240814---for【issues/7028】表格全屏后操作列中的下拉菜单和气泡确认框不显示
if (!popConfirm.getPopupContainer) {
popConfirm.getPopupContainer = () => {
return (table as any)?.wrapRef?.value ?? document.body;
};
}
// update-end--author:liaozhiyang---date:20240814---for【issues/7028】表格全屏后操作列中的下拉菜单和气泡确认框不显示
}
// update-end--author:liaozhiyang---date:20240105---for【issues/951】table删除记录时按钮显示错位
// update-begin--author:liaozhiyang---date:20240108---for【issues/936】表格操作栏删除当接口失败时气泡确认框不会消失
@ -196,8 +228,12 @@
});
isInButton && e.stopPropagation();
}
return { prefixCls, getActions, getDropdownList, getDropdownSlotList, getAlign, onCellClick, getTooltip, dropdownCls };
// update-begin--author:liaozhiyang---date:20240814---for【issues/7028】表格全屏后操作列中的下拉菜单和气泡确认框不显示
const dropdownGetPopupContainer = () => {
return (table as any)?.wrapRef?.value ?? document.body;
};
// update-end--author:liaozhiyang---date:20240814---for【issues/7028】表格全屏后操作列中的下拉菜单和气泡确认框不显示
return { prefixCls, getActions, getDropdownList, getDropdownSlotList, getAlign, onCellClick, getTooltip, dropdownCls, dropdownGetPopupContainer };
},
});
</script>

View File

@ -91,7 +91,7 @@
</template>
<script lang="ts">
import type { BasicColumn, ColumnChangeParam } from '../../types/table';
import { defineComponent, ref, reactive, toRefs, watchEffect, nextTick, unref, computed } from 'vue';
import { defineComponent, ref, reactive, toRefs, watchEffect, nextTick, unref, computed, watch } from 'vue';
import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue';
import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface';
import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
@ -107,6 +107,7 @@
import { cloneDeep, omit } from 'lodash-es';
import Sortablejs from 'sortablejs';
import type Sortable from 'sortablejs';
import { useLocaleStoreWithOut } from '/@/store/modules/locale';
interface State {
checkAll: boolean;
@ -156,6 +157,10 @@
const columnListRef = ref<ComponentRef>(null);
const restAfterOptions = {
value: null,
};
const state = reactive<State>({
checkAll: true,
checkedList: [],
@ -181,7 +186,7 @@
let sortable: Sortable;
const sortableOrder = ref<string[]>();
const localeStore = useLocaleStoreWithOut();
// 列表字段配置缓存
const { saveSetting, resetSetting } = useColumnsCache(
{
@ -191,6 +196,7 @@
plainSortOptions,
sortableOrder,
checkIndex,
restAfterOptions,
},
setColumns,
handleColumnFixed
@ -210,6 +216,14 @@
checkIndex.value = !!values.showIndexColumn;
checkSelect.value = !!values.rowSelection;
});
// update-begin--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
watch(localeStore, () => {
const columns = getColumns();
plainOptions.value = columns;
plainSortOptions.value = columns;
cachePlainOptions.value = columns;
});
// update-end--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
function getColumns() {
const ret: Options[] = [];
@ -227,7 +241,7 @@
const columns = getColumns();
const checkList = table
.getColumns({ ignoreAction: true })
.getColumns({ ignoreAction: true, ignoreIndex: true })
.map((item) => {
if (item.defaultHidden) {
return '';
@ -255,6 +269,9 @@
}
state.isInit = true;
state.checkedList = checkList;
// update-begin--author:liaozhiyang---date:20240612---for【TV360X-105】列展示设置问题[列展示如果存在未勾选的列,保存并刷新后,列展示复选框样式会错乱]
state.checkAll = columns.length === checkList.length;
// update-end--author:liaozhiyang---date:20240612---for【TV360X-105】列展示设置问题[列展示如果存在未勾选的列,保存并刷新后,列展示复选框样式会错乱]
}
// checkAll change
@ -272,7 +289,9 @@
const indeterminate = computed(() => {
const len = plainOptions.value.length;
let checkedLen = state.checkedList.length;
unref(checkIndex) && checkedLen--;
// update-begin--author:liaozhiyang---date:20240612---for【TV360X-105】列展示设置问题[列展示复选框不应该判断序号列复选框的状态]
// unref(checkIndex) && checkedLen--;
// update-end--author:liaozhiyang---date:20240612---for【TV360X-105】列展示设置问题[列展示复选框不应该判断序号列复选框的状态]
return checkedLen > 0 && checkedLen < len;
});
@ -289,23 +308,29 @@
// reset columns
function reset() {
// state.checkedList = [...state.defaultCheckList];
// update-begin--author:liaozhiyang---date:20231103---for【issues/825】tabel的列设置隐藏列保存后切换路由问题[重置没勾选]
state.checkedList = table
.getColumns({ ignoreAction: true })
.map((item) => {
return item.dataIndex || item.title;
})
.filter(Boolean) as string[];
// update-end--author:liaozhiyang---date:20231103---for【issues/825】tabel的列设置隐藏列保存后切换路由问题[重置没勾选]
state.checkAll = true;
plainOptions.value = unref(cachePlainOptions);
plainSortOptions.value = unref(cachePlainOptions);
// update-begin--author:liaozhiyang---date:20240612---for【TV360X-105】列展示设置问题[需要重置两次才回到初始状态]
setColumns(table.getCacheColumns());
if (sortableOrder.value) {
sortable.sort(sortableOrder.value);
}
resetSetting();
setTimeout(() => {
const columns = getColumns();
// state.checkedList = [...state.defaultCheckList];
// update-begin--author:liaozhiyang---date:20231103---for【issues/825】tabel的列设置隐藏列保存后切换路由问题[重置没勾选]
state.checkedList = table
.getColumns({ ignoreAction: true })
.map((item) => {
return item.dataIndex || item.title;
})
.filter(Boolean) as string[];
// update-end--author:liaozhiyang---date:20231103---for【issues/825】tabel的列设置隐藏列保存后切换路由问题[重置没勾选]
state.checkAll = true;
plainOptions.value = unref(cachePlainOptions);
plainSortOptions.value = unref(cachePlainOptions);
restAfterOptions.value = columns;
if (sortableOrder.value) {
sortable.sort(sortableOrder.value);
}
resetSetting();
}, 100);
// update-end--author:liaozhiyang---date:20240612---for【TV360X-105】列展示设置问题[需要重置两次才回到初始状态]
}
// Open the pop-up window for drag and drop initialization
@ -350,6 +375,9 @@
// setColumns(columns);
// update-end--author:liaozhiyang---date:20240522---for【TV360X-108】刷新后勾选之前未勾选的字段拖拽之后该字段对应的表格列消失了
// update-end--author:liaozhiyang---date:20230904---for【QQYUN-6424】table字段列表设置不显示后再拖拽字段顺序原本不显示的又显示了
// update-begin--author:liaozhiyang---date:20240611---for【TV360X-105】列展示设置问题[重置之后保存的顺序还是上次的]
restAfterOptions.value = null;
// update-end--author:liaozhiyang---date:20240611---for【TV360X-105】列展示设置问题[重置之后保存的顺序还是上次的]
},
});
// 记录原始 order 序列

View File

@ -56,14 +56,23 @@ function handleIndexColumn(propsRef: ComputedRef<BasicTableProps>, getPagination
columns.splice(indIndex, 1);
}
});
// update-begin--author:liaozhiyang---date:20240611---for【TV360X-105】列展示设置问题[列展示复选框不应该判断序号列复选框的状态]
if (columns.length === 0 && showIndexColumn) {
const indIndex = columns.findIndex((column) => column.flag === INDEX_COLUMN_FLAG);
if (indIndex === -1) {
pushIndexColumns = true;
}
}
// update-end--author:liaozhiyang---date:20240611---for【TV360X-105】列展示设置问题[列展示复选框不应该判断序号列复选框的状态]
if (!pushIndexColumns) return;
const isFixedLeft = columns.some((item) => item.fixed === 'left');
columns.unshift({
flag: INDEX_COLUMN_FLAG,
width: 50,
// update-begin--author:liaozhiyang---date:20240724---for【TV360X-1634】密度是宽松模式时序号列表头换行了
width: propsRef.value.size === 'large' ? 65 : 50,
// update-end--author:liaozhiyang---date:20240724---for【TV360X-1634】密度是宽松模式时序号列表头换行了
title: t('component.table.index'),
align: 'center',
customRender: ({ index }) => {
@ -107,7 +116,13 @@ export function useColumns(
const getColumnsRef = computed(() => {
const columns = cloneDeep(unref(columnsRef));
// update-begin--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
if (isArray(columns)) {
columns.forEach((item) => {
item.title = isFunction(item.title) ? item.title() : item.title;
});
}
// update-end--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
handleIndexColumn(propsRef, getPaginationRef, columns);
handleActionColumn(propsRef, columns);
// update-begin--author:sunjianlei---date:220230630---for【QQYUN-5571】自封装选择列解决数据行选择卡顿问题
@ -346,3 +361,4 @@ export function formatCell(text: string, format: CellFormat, record: Recordable,
return text;
}
}

View File

@ -97,7 +97,14 @@ export function useColumnsCache(opt, setColumns, handleColumnFixed) {
/** 保存列配置 */
function saveSetting() {
const { checkedList } = opt.state;
const sortedList = unref(opt.plainSortOptions).map((item) => item.value);
// update-begin--author:liaozhiyang---date:20240611---for【TV360X-105】列展示设置问题[重置之后保存的顺序还是上次的]
let sortedList = [];
if (opt.restAfterOptions.value) {
sortedList = opt.restAfterOptions.value.map((item) => item.value);
} else {
sortedList = unref(opt.plainSortOptions).map((item) => item.value);
}
// update-end--author:liaozhiyang---date:20240611---for【TV360X-105】列展示设置问题[重置之后保存的顺序还是上次的]
$ls.set(cacheKey.value, {
// 保存的列
checkedList,

View File

@ -219,22 +219,27 @@ export function useCustomSelection(
});
// 选择全部
function onSelectAll(checked: boolean) {
function onSelectAll(checked: boolean, flag = 'currentPage') {
// update-begin--author:liaozhiyang---date:20231122---for【issues/5577】BasicTable组件全选和取消全选时不触发onSelectAll事件
if (unref(propsRef)?.rowSelection?.onSelectAll) {
allSelected = checked;
changeRows = getInvertRows(selectedRows.value);
changeRows = getInvertRows(selectedRows.value, checked, flag);
}
// update-end--author:liaozhiyang---date:20231122---for【issues/5577】BasicTable组件全选和取消全选时不触发onSelectAll事件
// 取消全选
if (!checked) {
// update-begin--author:liaozhiyang---date:20240510---for【issues/1173】取消全选只是当前页面取消
// selectedKeys.value = [];
// selectedRows.value = [];
// emitChange('all');
flattedData.value.forEach((item) => {
updateSelected(item, false);
});
// update-begin--author:liaozhiyang---date:20240808---for【issues/6958】取消没触发onSelectAll事件跨页选中后 changeRows 为空
if (flag === 'allPage') {
selectedKeys.value = [];
selectedRows.value = [];
} else {
flattedData.value.forEach((item) => {
updateSelected(item, false);
});
}
emitChange('all');
// update-end--author:liaozhiyang---date:20240808---for【issues/6958】取消没触发onSelectAll事件跨页选中后 changeRows 为空
// update-end--author:liaozhiyang---date:20240510---for【issues/1173】取消全选只是当前页面取消
return;
}
@ -310,6 +315,7 @@ export function useCustomSelection(
// 选中单个
function onSelect(record, checked) {
onSelectChild(record, checked);
updateSelected(record, checked);
emitChange();
}
@ -359,7 +365,25 @@ export function useCustomSelection(
}
// update-end--author:liaozhiyang---date:20231122---for【issues/5577】BasicTable组件全选和取消全选时不触发
}
// update-begin--author:liusq---date:20240819---for树形表格设置层级关联不生效
/**
* 层级关联时,选中下级数据
* @param record
* @param checked
*/
function onSelectChild(record, checked) {
if (unref(propsRef)?.isTreeTable && unref(propsRef)?.rowSelection?.checkStrictly && !isRadio.value) {
if (record[childrenColumnName.value] && record[childrenColumnName.value].length > 0) {
record[childrenColumnName.value].forEach((children) => {
updateSelected(children, checked);
if (children[childrenColumnName.value] && children[childrenColumnName.value].length > 0) {
onSelectChild(children, checked);
}
});
}
}
}
// update-end--author:liusq---date:20240819---for树形表格设置层级关联不生效
// 用于判断是否是自定义选择列
function isCustomSelection(column: BasicColumn) {
return column.key === CUS_SEL_COLUMN_KEY;
@ -481,7 +505,7 @@ export function useCustomSelection(
// 清空所有选择
function clearSelectedRowKeys() {
onSelectAll(false);
onSelectAll(false, 'allPage');
}
// 通过 selectedKeys 同步 selectedRows
@ -536,28 +560,30 @@ export function useCustomSelection(
return false;
}
/**
*2023-11-22
*2024-08-08
*廖志阳
*根据全选或者反选返回源数据中这次需要变更的数据
*根据全选或者反选(或者使用clearSelectedRowKeys()方法)返回源数据中这次需要变更的数据
*/
function getInvertRows(rows: any): any {
const allRows = findNodeAll(toRaw(unref(flattedData)), () => true, {
children: propsRef.value.childrenColumnName ?? 'children',
});
if (rows.length === 0 || rows.length === allRows.length) {
return allRows;
} else {
const allRowsKey = allRows.map((item) => getRecordKey(item));
rows.forEach((rItem) => {
const rItemKey = getRecordKey(rItem);
const findIndex = allRowsKey.findIndex((item) => rItemKey === item);
if (findIndex != -1) {
allRowsKey.splice(findIndex, 1);
allRows.splice(findIndex, 1);
function getInvertRows(selectedRows: any, checked: boolean, flag): any {
if (flag == 'currentPage') {
const curPageRows = findNodeAll(toRaw(unref(flattedData)), () => true, {
children: propsRef.value.childrenColumnName ?? 'children',
});
const selectedkeys = selectedRows.map((item) => getRecordKey(item));
const result: any = [];
curPageRows.forEach((item) => {
const curRowkey = getRecordKey(item);
const index = selectedkeys.findIndex((item) => item === curRowkey);
if (index == -1) {
checked && result.push(toRaw(item));
} else {
!checked && result.push(toRaw(item));
}
});
return result;
} else {
return toRaw(selectedRows);
}
return allRows;
}
function getSelectRows<T = Recordable>() {
return unref(selectedRows) as T[];

View File

@ -426,6 +426,9 @@ export interface BasicColumn extends ColumnProps<Recordable> {
//
flag?: 'INDEX' | 'DEFAULT' | 'CHECKBOX' | 'RADIO' | 'ACTION';
// update-begin--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
title: string | Fn;
// update-end--author:liaozhiyang---date:20240724---for【issues/6908】多语言无刷新切换时BasicColumn和FormSchema里面的值不能正常切换
customTitle?: VueNode;
slots?: Recordable;
@ -465,6 +468,8 @@ export interface BasicColumn extends ColumnProps<Recordable> {
column: BasicColumn;
}) => any | VNodeChild | JSX.Element;
// update-end--author:liaozhiyang---date:20240425---for【pull/1201】添加antd的TableSummary功能兼容老的summary表尾合计
// 额外的属性
extraProps?: Recordable;
}
export type ColumnChangeParam = {

View File

@ -29,4 +29,5 @@ export interface PopConfirm {
icon?: string;
placement?: string;
overlayClassName?: string;
getPopupContainer: Fn;
}

View File

@ -5,7 +5,6 @@
<ImgUpload
:fullscreen="fullscreen"
@uploading="handleImageUploading"
@loading="handleLoading"
@done="handleDone"
v-show="editorRef"
:disabled="disabled"
@ -14,7 +13,6 @@
<!-- update-end--author:liaozhiyang---date:20240517---forTV360X-35富文本图片上传遮挡其他按钮 -->
<Editor :id="tinymceId" ref="elRef" :disabled="disabled" :init="initOptions" :style="{ visibility: 'hidden' }" v-if="!initOptions.inline"></Editor>
<slot v-else></slot>
<ProcessMask ref="processMaskRef" :show="showUploadMask"/>
</div>
</template>
@ -35,7 +33,6 @@
import 'tinymce/plugins/image';
import { defineComponent, computed, nextTick, ref, unref, watch, onDeactivated, onBeforeUnmount, onMounted } from 'vue';
import ImgUpload from './ImgUpload.vue';
import ProcessMask from './ProcessMask.vue';
import {simpleToolbar, menubar, simplePlugins} from './tinymce';
import { buildShortUUID } from '/@/utils/uuid';
import { bindHandlers } from './helper';
@ -85,10 +82,6 @@
type: Boolean,
default: true,
},
showUploadMask: {
type: Boolean,
default: false,
},
//是否聚焦
autoFocus:{
type: Boolean,
@ -98,9 +91,9 @@
export default defineComponent({
name: 'Tinymce',
components: { ImgUpload,Editor,ProcessMask },
components: { ImgUpload,Editor },
inheritAttrs: false,
props: tinymceProps as any,
props: tinymceProps,
emits: ['change', 'update:modelValue', 'inited', 'init-error'],
setup(props, { emit, attrs }) {
console.log("---Tinymce---初始化---")
@ -110,7 +103,6 @@
const tinymceId = ref<string>(buildShortUUID('tiny-vue'));
const elRef = ref<Nullable<HTMLElement>>(null);
const editorRootRef = ref<Nullable<HTMLElement>>(null);
const processMaskRef = ref<any>(null);
const imgUploadShow = ref(false);
const targetElem = ref<null | HTMLDivElement>(null);
@ -333,20 +325,6 @@
setValue(editor, val);
}
/**
* 上传进度计算
* @param file
* @param fileList
*/
function handleLoading(fileLength,showMask){
if(fileLength && fileLength > 0){
setTimeout(() => {
props?.showUploadMask && processMaskRef.value.calcProcess(fileLength)
},100)
}else{
props?.showUploadMask && (processMaskRef.value.showMask = showMask);
}
}
function getUploadingImgName(name: string) {
return `[uploading:${name}]`;
}
@ -419,9 +397,6 @@
editorRootRef,
imgUploadShow,
targetElem,
handleLoading,
processMaskRef
};
},
});
@ -453,7 +428,6 @@
}
// update-end--author:liaozhiyang---date:20240527---for【TV360X-329】富文本禁用状态下工具栏划过边框丢失
}
html[data-theme='dark'] {
.@{prefix-cls} {
.tox .tox-edit-area__iframe {background-color: #141414;}

View File

@ -8,7 +8,6 @@
:showUploadList="false"
:data="getBizData()"
:headers="getheader()"
:before-upload="beforeUpload"
accept=".jpg,.jpeg,.gif,.png,.webp"
>
<a-button type="primary" v-bind="{ ...getButtonProps }">
@ -38,8 +37,10 @@
default: false,
},
},
emits: ['uploading', 'done', 'error', 'loading'],
emits: ['uploading', 'done', 'error'],
setup(props, { emit }) {
let uploading = false;
//update-begin-author:taoyan date:2022-5-13 for: 富文本上传图片不支持
function getheader() {
return getHeaders();
@ -66,37 +67,33 @@
};
});
let uploadLength = 0;
function handleChange({ file, fileList }) {
// 过滤掉已经存在的文件
fileList = fileList.filter((file) => {
const existFile = uploadFileList.value.find(({ uid }) => uid === file.uid);
return existFile ? false : true;
});
uploadLength == 0 && (uploadLength = fileList.length);
if (file.status != 'uploading') {
emit('loading', uploadLength, true);
if (file.status === 'error') {
emit('error');
uploading = false;
}
// 处理上传好的文件
let files = [] as any;
let noUploadingFileCount = 0;
if (file.status != 'uploading') {
fileList.forEach((file) => {
if (file.status === 'done' && file.response.success) {
const name = file?.name;
let realUrl = getFileAccessHttpUrl(file.response.message);
uploadFileList.value.push(file);
emit('done', name, realUrl);
files.push(file);
}
if (file.status != 'uploading') {
noUploadingFileCount++;
}
});
}
if (noUploadingFileCount == fileList.length) {
fileList.forEach((file) => {
const name = file?.name;
let realUrl = getFileAccessHttpUrl(file.response.message);
emit('done', name, realUrl);
});
}
}
//上传之前
function beforeUpload() {
uploadLength = 0;
emit('loading', null, true);
setTimeout(() => {
emit('loading', null, false);
}, 10000);
}
return {
prefixCls,
handleChange,
@ -105,8 +102,7 @@
getBizData,
t,
getButtonProps,
uploadFileList,
beforeUpload,
uploadFileList
};
},
});

View File

@ -1,99 +0,0 @@
<template>
<div class="mask" v-if="showMask && show">
<div class="progress-bar-rear">
<div class="progress-bar-front" :style="{ width: progressBarWidth }"></div>
</div>
<div class="value">{{ percentage }}</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
const props = defineProps({
backColor: {
type: [String],
default: 'white',
},
processColor: {
type: String,
default: '#018FFB',
},
show: {
type: Boolean,
default: false,
},
});
//显示遮罩
const showMask = ref(false);
//进度值占比
const progressValue = ref<any>(0);
//当前数量
const currentNum = ref(0);
// 计算进度条宽度的计算属性
const progressBarWidth = computed(() => {
return progressValue.value > 0 ? `${progressValue.value}px` : '0px';
});
// 计算进度条百分比
const percentage = computed(() => {
return `${progressValue.value}%`;
});
// 进度色
const frontColor = computed(() => {
return props.processColor;
});
// 后置背景色
const rearColor = computed(() => {
return props.backColor;
});
function calcProcess(totalNum) {
!showMask.value && (showMask.value = true);
currentNum.value += 1;
progressValue.value = ((currentNum.value / totalNum) * 100).toFixed(2);
console.log('currentNum.value', currentNum.value);
console.log('totalNum.value', totalNum);
if (currentNum.value == totalNum) {
showMask.value = false;
currentNum.value = 0;
progressValue.value = 0;
}
}
defineExpose({
calcProcess,
showMask,
});
</script>
<style lang="less">
.mask {
position: absolute; /* 或者使用固定定位等其他方式 */
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5); /* 半透明遮罩 */
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
z-index: 99;
}
.progress-bar-rear {
width: 100px; /* 进度条宽度 */
height: 10px; /* 进度条高度 */
background-color: v-bind(rearColor); /* 进度条颜色 */
border-radius: 4px;
}
.progress-bar-front {
height: 10px; /* 进度条高度 */
background-color: v-bind(frontColor); /* 进度条颜色 */
border-radius: 4px;
}
.value {
color: #fff;
margin-left: 5px;
font-size: 16px;
font-weight: 600;
}
</style>

View File

@ -201,7 +201,7 @@
// 点击开始上传
async function handleStartUpload() {
const { maxNumber } = props;
if ((fileListRef.value.length + props.previewFileList?.length ?? 0) > maxNumber) {
if ((fileListRef.value.length + (props.previewFileList?.length ?? 0)) > maxNumber) {
return createMessage.warning(t('component.upload.maxNumber', [maxNumber]));
}
try {

View File

@ -158,6 +158,7 @@
import '../style/style.less';
const props = defineProps(['chatData', 'uuid', 'dataSource']);
const emit = defineEmits(['save']);
const { scrollRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll();
const prompt = ref<string>('');
const loading = ref<boolean>(false);
@ -335,6 +336,7 @@
try {
return await new Promise<void>((resolve) => {
props.chatData.length = 0;
emit('save');
resolve();
});
} catch {

View File

@ -102,6 +102,7 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
const props = defineProps(['dataSource']);
const emit = defineEmits(['save']);
const inputRef = ref(null);
let inputValue = '';
//新建聊天
@ -161,6 +162,7 @@
// 删没了(删除了最后一个)
props.dataSource.active = null;
}
emit('save');
}
};
watch(

View File

@ -3,7 +3,7 @@
<template v-if="dataSource">
<div class="leftArea" :class="[expand ? 'expand' : 'shrink']">
<div class="content">
<slide v-if="uuid" :dataSource="dataSource"></slide>
<slide v-if="uuid" :dataSource="dataSource" @save="handleSave"></slide>
</div>
<div class="toggle-btn" @click="handleToggle">
<span class="icon">
@ -17,7 +17,7 @@
</div>
</div>
<div class="rightArea" :class="[expand ? 'expand' : 'shrink']">
<chat v-if="uuid && chatVisible" :uuid="uuid" :chatData="chatData" :dataSource="dataSource"></chat>
<chat v-if="uuid && chatVisible" :uuid="uuid" :chatData="chatData" :dataSource="dataSource" @save="handleSave"></chat>
</div>
</template>
<Spin v-else :spinning="true"></Spin>
@ -84,6 +84,14 @@
const save = (content) => {
defHttp.post({ url: configUrl.save, params: { content: JSON.stringify(content) } }, { isTransformResponse: false });
};
const handleSave = () => {
// 删除标签或清空内容之后的保存
save(dataSource.value);
setTimeout(() => {
// 删除标签或清空内容也会触发watch保存此时不需watch保存需清除
clearTimeout(timer);
}, 50);
};
// 监听dataSource变化执行操作
const execute = () => {
unwatch01 = watch(

View File

@ -5,15 +5,15 @@
<template #buttons>
<div :class="`${prefixCls}-button div`" :size="btnSize">
<slot v-if="showPrefix" name="toolbarPrefix" :size="btnSize" />
<a-button v-if="showAdd" type="primary" preIcon="ant-design:plus-outlined" :disabled="disabled" @click="trigger('add')">
<a-button v-if="showAdd" type="primary" preIcon="ant-design:plus-outlined" :disabled="disabled" :loading="deleting" @click="trigger('add')">
<span>新增</span>
</a-button>
<a-button v-if="showSave" preIcon="ant-design:save-outlined" :disabled="disabled" @click="trigger('save')">
<span>保存</span>
</a-button>
<template v-if="selectedRowIds.length > 0">
<Popconfirm v-if="showRemove" :title="`确定要删除这 ${selectedRowIds.length} 项吗?`" @confirm="trigger('remove')">
<a-button preIcon="ant-design:minus-outlined" :disabled="disabled">删除</a-button>
<template v-if="deleting || selectedRowIds.length > 0">
<Popconfirm v-if="showRemove" :title="`确定要删除这 ${selectedRowIds.length} 项吗?`" :disabled="deleting" @confirm="onRemove">
<a-button preIcon="ant-design:minus-outlined" :disabled="disabled" :loading="deleting">删除</a-button>
</Popconfirm>
<template v-if="showClearSelection">
<a-button preIcon="ant-design:delete-outlined" @click="trigger('clearSelection')">清空选择</a-button>
@ -115,4 +115,19 @@
function toggleCollapse() {
collapsed.value = !collapsed.value;
}
// 【TV360X-1975】在Online设计中当字段多时由于会同步删除其他表格导致删除时间变长所以增加删除loading防止以为点击删除按钮无效
const deleting = ref(false);
let deleteTimer: any = null
function onRemove() {
trigger('remove')
deleting.value = true;
if (deleteTimer) {
clearTimeout(deleteTimer)
}
deleteTimer = setTimeout(() => deleting.value = false, 300);
}
</script>

View File

@ -183,7 +183,9 @@ function handleSeqColumn({ props, col, columns }: HandleArgs) {
*/
function handleSelectionColumn({ props, data, col, columns }: HandleArgs) {
// 判断是否开启了可选择行
if (props.rowSelection) {
// -update-begin--author:liaozhiyang---date:20240617---for【TV360X-1002】详情页面行编辑不显示checkbox
if (props.rowSelection && props.disabled == false) {
// -update-end--author:liaozhiyang---date:20240617---for【TV360X-1002】详情页面行编辑不显示checkbox
let width = 45;
if (data.statistics.has && !props.rowExpand && !props.dragSort) {
width = 60;

View File

@ -1,6 +1,7 @@
import { onMounted, onUnmounted, nextTick } from 'vue';
import { JVxeTableMethods, JVxeTableProps } from '/@/components/jeecg/JVxeTable/src/types';
import Sortable from 'sortablejs';
import { isEnabledVirtualYScroll } from '/@/components/jeecg/JVxeTable/utils';
export function useDragSort(props: JVxeTableProps, methods: JVxeTableMethods) {
if (props.dragSort) {
@ -37,40 +38,73 @@ export function useDragSort(props: JVxeTableProps, methods: JVxeTableMethods) {
// @ts-ignore
startChildren = [...from.children];
},
onEnd(e) {
let oldIndex = e.oldIndex as number;
let newIndex = e.newIndex as number;
if (oldIndex === newIndex) {
return;
}
// 【VUEN-2505】获取当前行数据
let rowNode = xTable.getRowNode(e.item);
if (!rowNode) {
return;
}
let from = e.from;
let element = startChildren[oldIndex];
let target = null;
if (oldIndex > newIndex) {
// 向上移动
if (oldIndex + 1 < startChildren.length) {
target = startChildren[oldIndex + 1];
onEnd(e: any) {
// -update-begin--author:liaozhiyang---date:20240619---for【TV360X-585】拖动字段虚拟滚动不好使
const isRealEnabledVirtual = isEnabledVirtualYScroll(props, xTable);
let newIndex;
let oldIndex;
// 滚动排序需要区分当前行编辑是否启动了虚拟滚动(底层loadData方法对是否真实开启了虚拟滚动处理不一样导致需要区分)
if (isRealEnabledVirtual) {
// e.clone的元素才是真实拖动的元素(虚拟滚动也不会变)
const dragNode = e.clone;
const dragRowInfo = xTable.getRowNode(dragNode);
// e.item的元素只有没虚拟滚动时才是拖动的元素(如果虚拟滚动了则会变)
const itemNode = e.item;
const itemRowInfo = xTable.getRowNode(itemNode);
// e.newIndex是当前可视区内元素的索引(不是数据实际的索引)、e.oldIndex 是拖动时可视区内元素的索引(不是数据实际的索引)
if (dragRowInfo!.rowid === itemRowInfo!.rowid) {
// e.clone和e.item相同说明拖拽的元素在DOM中没被虚拟滚动给remove掉。
if (e.newIndex === e.oldIndex) {
// 此时新旧index一样就可认为没拖动
return;
}
} else {
}
// 此时真实DOM元素顺序已排(通过拖拽元素的前后元素确定拖拽元素在真实数据中是往前还是往后拖)
oldIndex = dragRowInfo!.index;
const len = e.from.childNodes.length;
let referenceIndex;
let referenceNode;
if (e.newIndex + 1 < len) {
// 拖拽DOM交换之后后面还有元素参考物是后面的元素
referenceNode = e.from.childNodes[e.newIndex + 1];
referenceIndex = xTable.getRowNode(referenceNode)!.index;
if (oldIndex > referenceIndex) {
newIndex = referenceIndex;
} else {
newIndex = referenceIndex - 1;
}
} else {
// 拖拽DOM交换之后后面没有元素了参考物是前面的元素
referenceNode = e.from.childNodes[e.newIndex - 1];
referenceIndex = xTable.getRowNode(referenceNode)!.index;
newIndex = referenceIndex;
}
} else {
// 向下移动
target = startChildren[oldIndex + 1];
}
from.removeChild(element);
from.insertBefore(element, target);
nextTick(() => {
// 【VUEN-2505】算出因虚拟滚动导致的偏移量
let diffIndex = rowNode!.index - oldIndex;
if (diffIndex > 0) {
oldIndex = oldIndex + diffIndex;
newIndex = newIndex + diffIndex;
oldIndex = e.oldIndex;
newIndex = e.newIndex;
if (oldIndex === newIndex) {
return;
}
const from = e.from;
const element = startChildren[oldIndex];
let target = null;
if (oldIndex > newIndex) {
// 向上移动
if (oldIndex + 1 < startChildren.length) {
target = startChildren[oldIndex + 1];
}
} else {
// 向下移动
target = startChildren[oldIndex + 1];
}
from.removeChild(element);
from.insertBefore(element, target);
}
// -update-end--author:liaozhiyang---date:20240620---for【TV360X-585】拖动字段虚拟滚动不好使
nextTick(() => {
methods.doSort(oldIndex, newIndex);
methods.trigger('dragged', { oldIndex, newIndex });
methods.trigger('dragged', { oldIndex: oldIndex, newIndex: newIndex });
});
},
});

View File

@ -11,6 +11,7 @@ import { useWebSocket } from './useWebSocket';
import { getPrefix, getJVxeAuths } from '../utils/authUtils';
import { excludeKeywords } from '../componentMap';
import { useColumnsCache } from './useColumnsCache';
import { isEnabledVirtualYScroll } from '/@/components/jeecg/JVxeTable/utils';
export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps, refs: JVxeRefs, instanceRef: Ref) {
let xTableTemp: VxeTableInstance & VxeTablePrivateMethods;
@ -195,7 +196,9 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
return getEnhanced(column.params.type).aopEvents.activeMethod!.apply(instanceRef.value, arguments as any) ?? true;
})();
if (!flag) {
getXTable().clearActived();
// -update-begin--author:liaozhiyang---date:20240619---for【TV360X-1404】vxetable警告
getXTable().clearEdit();
// -update-end--author:liaozhiyang---date:20240619---for【TV360X-1404】vxetable警告
}
return flag;
}
@ -424,8 +427,10 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
// 插入行
let result = await xTable.insertAt(rows, index);
if (setActive) {
// -update-begin--author:liaozhiyang---date:20240619---for【TV360X-1404】vxetable警告
// 激活最后一行的编辑模式
xTable.setActiveRow(result.rows[result.rows.length - 1]);
xTable.setEditRow(result.rows[result.rows.length - 1]);
// -update-end--author:liaozhiyang---date:20240619---for【TV360X-1404】vxetable警告
}
await recalcSortNumber();
return result;
@ -453,8 +458,14 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
callback('', tableData);
}
type getTableDataOptions = {
rowIds?: string[];
// 是否保留新行的id
keepNewId?: boolean;
}
/** 获取表格数据 */
function getTableData(options: any = {}) {
function getTableData(options: getTableDataOptions = {}) {
let { rowIds } = options;
let tableData;
// 仅查询指定id的行
@ -470,7 +481,10 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
// 查询所有行
tableData = getXTable().getTableData().fullData;
}
return filterNewRows(tableData, false);
return filterNewRows(tableData, {
keepNewId: options.keepNewId ?? false,
removeNewLine: false,
});
}
/** 仅获取新增的数据 */
@ -513,23 +527,33 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
return null;
}
type filterNewRowsOptions = {
keepNewId?: boolean;
removeNewLine?: boolean;
} | boolean
/**
* 过滤添加的行
* @param rows 要筛选的行数据
* @param remove true = 删除新增false=只删除id
* @param optOrRm 如果传 boolean 则是 removeNewLine 参数(true = 删除新增false=只删除id),如果传对象则是配置参数
* @param handler function
*/
function filterNewRows(rows, remove = true, handler?: Fn) {
function filterNewRows(rows, optOrRm:filterNewRowsOptions = true, handler?: Fn) {
let insertRecords = getXTable().getInsertRecords();
let records: Recordable[] = [];
optOrRm = typeof optOrRm === 'boolean' ? { removeNewLine: optOrRm } : optOrRm;
// true = 删除新增false=只删除id
let removeNewLine = optOrRm?.removeNewLine ?? true;
for (let row of rows) {
let item = cloneDeep(row);
if (insertRecords.includes(row)) {
handler ? handler({ item, row, insertRecords }) : null;
if (remove) {
if (removeNewLine) {
continue;
}
delete item.id;
if (!optOrRm?.keepNewId) {
delete item.id;
}
}
records.push(item);
}
@ -762,6 +786,11 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
if (xTable.keepSource) {
sort(xTable.internalData.tableSourceData);
}
// -update-begin--author:liaozhiyang---date:20240620---for【TV360X-585】拖动字段虚拟滚动不好使
if (isEnabledVirtualYScroll(props, xTable)) {
await xTable.loadData(xTable.internalData.tableFullData);
}
// -update-end--author:liaozhiyang---date:20240620---for【TV360X-585】拖动字段虚拟滚动不好使
return await recalcSortNumber(force);
}
}

View File

@ -107,3 +107,26 @@ export function vModel(value, row, column: Ref<any> | string) {
let property = isRef(column) ? column.value.property : column;
unref(row)[property] = value;
}
/**
* liaozhiyang
* 2024-06-20
* 判断当前行编辑是否使用了虚拟滚动并不是开启了就是还得满足数据数量大于gt值
*/
export function isEnabledVirtualYScroll(props, xTable): boolean {
let isRealEnabledVirtual = false;
const isEnabledVScroll = props?.scrollY?.enabled;
// 100是底层的默认值
const gtYNum = props?.scrollY?.gt || 100;
if (isEnabledVScroll) {
const tableFullData = xTable.internalData.tableFullData;
if (gtYNum === 0) {
isRealEnabledVirtual = true;
} else {
if (tableFullData.length > gtYNum) {
isRealEnabledVirtual = true;
}
}
}
return isRealEnabledVirtual;
}

View File

@ -48,6 +48,12 @@
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
</template>
<template #bodyCell="{ text, column }">
<template v-if="column.fieldType === 'Image'">
<span v-if="!text" style="font-size: 12px; font-style: italic">无图片</span>
<img v-else :src="getImgView(text)" alt="图片不存在" class="cellIamge" @click="viewOnlineCellImage($event, text)" />
</template>
</template>
</BasicTable>
<!-- 跳转Href的动态组件方式 -->
@ -64,7 +70,9 @@
import { usePopBiz } from '/@/components/jeecg/OnLine/hooks/usePopBiz';
import { useMessage } from '/@/hooks/web/useMessage';
import { useRoute } from 'vue-router';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { createImgPreview } from '/@/components/Preview/index';
export default defineComponent({
name: 'JPopupOnlReport',
components: {
@ -198,6 +206,40 @@
loadData(1);
}
/**
* 2024-07-24
* liaozhiyang
* 【TV360X-1756】报表添加图片类型
* 图片
* @param text
*/
function getImgView(text) {
if (text && text.indexOf(',') > 0) {
text = text.substring(0, text.indexOf(','));
}
return getFileAccessHttpUrl(text);
}
/**
* 2024-07-24
* liaozhiyang
* 【TV360X-1756】报表添加图片类型
* 预览列表 cell 图片
* @param text
*/
function viewOnlineCellImage(e, text) {
e.stopPropagation();
if (text) {
let imgList: any = [];
let arr = text.split(',');
for (let str of arr) {
if (str) {
imgList.push(getFileAccessHttpUrl(str));
}
}
createImgPreview({ imageList: imgList });
}
}
return {
attrs,
@ -229,6 +271,8 @@
searchQuery,
searchReset,
onExportXls,
getImgView,
viewOnlineCellImage,
};
},
});
@ -251,4 +295,12 @@
:deep(.ant-select-selector){
min-width: 95px;
}
.cellIamge {
height: 25px !important;
margin: 0 auto;
max-width: 80px;
font-size: 12px;
font-style: italic;
cursor: pointer;
}
</style>

View File

@ -9,6 +9,7 @@ import { useRouter, useRoute } from 'vue-router';
import { useMethods } from '/@/hooks/system/useMethods';
import { importViewsFile, _eval } from '/@/utils';
import {getToken} from "@/utils/auth";
import {replaceUserInfoByExpression} from "@/utils/common/compUtils";
export function usePopBiz(ob, tableRef?) {
// update-begin--author:liaozhiyang---date:20230811---for【issues/675】子表字段Popup弹框数据不更新
@ -188,7 +189,8 @@ export function usePopBiz(ob, tableRef?) {
* 加载列信息
*/
function loadColumnsInfo() {
let url = `${configUrl.getColumns}${props.code}`;
const {code} = handleCodeParams(true)
let url = `${configUrl.getColumns}${code}`;
//缓存key
let groupIdKey = props.groupId ? `${props.groupId}${url}` : '';
httpGroupRequest(() => defHttp.get({ url }, { isTransformResponse: false, successMessageMode: 'none' }), groupIdKey).then((res) => {
@ -240,6 +242,11 @@ export function usePopBiz(ob, tableRef?) {
// 第一次加载 置空isTotal 在这里调用确保 该方法只是进入页面后 加载一次 其余查询不走该方法
pagination.isTotal = '';
let url = `${configUrl.getColumnsAndData}${props.id}`;
const {query} = handleCodeParams()
if (query) {
url = url + query
}
//缓存key
let groupIdKey = props.groupId ? `${props.groupId}${url}` : '';
httpGroupRequest(() => defHttp.get({ url }, { isTransformResponse: false, successMessageMode: 'none' }), groupIdKey).then((res) => {
@ -281,6 +288,39 @@ export function usePopBiz(ob, tableRef?) {
});
}
// 处理动态参数和系统变量
function handleCodeParams(onlyCode: boolean = false) {
if (!props.code) {
return {code: '', query: ''}
}
const firstIndex = props.code.indexOf('?')
if (firstIndex === -1) {
return {code: props.code, query: ''}
}
const code = props.code.substring(0, firstIndex)
if (onlyCode) {
return {code: code, query: ''}
}
const queryOrigin = props.code.substring(firstIndex, props.code.length);
let query: string
// 替换系统变量
query = replaceUserInfoByExpression(queryOrigin)
// 获取表单值
if (typeof props.getFormValues === 'function') {
const values = props.getFormValues()
// 替换动态参数,如果有 ${xxx} 则替换为实际值
query = query.replace(/\${([^}]+)}/g, (_$0, $1) => {
if (values[$1] == null) {
return ''
}
return values[$1]
});
}
return {code, query, queryOrigin}
}
/**
* 处理求和的列 合计逻辑 [待优化 3.0]
*/
@ -502,10 +542,15 @@ export function usePopBiz(ob, tableRef?) {
// 【VUEN-1568】如果选中了某些行就只导出选中的行
let keys = unref(checkedKeys);
if (keys.length > 0) {
params['force_id'] = keys
keys = keys
.map((i) => selectRows.value.find((item) => combineRowKey(item) === i)?.id)
.filter((i) => i != null && i !== '')
.join(',');
.filter((i) => i != null && i !== '');
// 判断是否有ID字段
if (keys.length === 0) {
createMessage.warning('由于数据中缺少ID字段故无法使用选中导出功能');
return;
}
params['force_id'] = keys.join(',');
}
handleExportXls(title.value, url, params);
}
@ -592,6 +637,10 @@ export function usePopBiz(ob, tableRef?) {
// update-begin--author:liaozhiyang---date:20240603---for【TV360X-578】online报表SQL翻译第二页不翻页数据
let url = `${configUrl.getColumnsAndData}${unref(cgRpConfigId)}`;
// update-end--author:liaozhiyang---date:20240603---for【TV360X-578】online报表SQL翻译第二页不翻页数据
const {query} = handleCodeParams()
if (query) {
url = url + query
}
//缓存key
let groupIdKey = props.groupId ? `${props.groupId}${url}${JSON.stringify(params)}` : '';
httpGroupRequest(() => defHttp.get({ url, params }, { isTransformResponse: false, successMessageMode: 'none' }), groupIdKey).then((res) => {

View File

@ -87,6 +87,7 @@
HistoryFileList,
},
props: {
tableId: propTypes.string.def(''),
tableName: propTypes.string.def(''),
dataId: propTypes.string.def(''),
datetime: propTypes.number.def(1)
@ -146,14 +147,15 @@
function onSelectFileOk(temp) {
// update-begin--author:liaozhiyang---date:20240603---for【TV360X-935】从知识库选择文件判断下是否没选
if (temp.id === '') return;
if (temp.length === 0) return;
// update-end--author:liaozhiyang---date:20240603---for【TV360X-935】从知识库选择文件判断下是否没选
let arr = selectFileList.value;
arr.push({
...temp,
exist: true
})
selectFileList.value = arr;
// -update-begin--author:liaozhiyang---date:20240614---for【TV360X-938】知识库文件选择支持多选
temp.forEach((item) => {
item.exist = true;
});
selectFileList.value = [...arr, ...temp];
// -update-end--author:liaozhiyang---date:20240614---for【TV360X-938】知识库文件选择支持多选
}
return {

View File

@ -17,7 +17,7 @@
<span>{{ item.toUserId_dictText }}</span>
<Tooltip class="comment-last-content" @openChange="(v)=>visibleChange(v, item)">
<template #title>
<div v-html="getHtml(item.commentId_dictText)"></div>
<div v-html="getHtml(lineFeed(item.commentId_dictText))"></div>
</template>
<message-outlined />
</Tooltip>
@ -42,7 +42,7 @@
</template>
<template #content>
<div class="content" v-html="getHtml(item.commentContent)" style="font-size: 15px">
<div class="content" v-html="getHtml(lineFeed(item.commentContent))" style="font-size: 15px">
</div>
<div v-if="item.fileList && item.fileList.length > 0">
@ -105,6 +105,7 @@
HistoryFileList,
},
props: {
tableId: propTypes.string.def(''),
tableName: propTypes.string.def(''),
dataId: propTypes.string.def(''),
datetime: propTypes.number.def(1),
@ -281,6 +282,11 @@
}
}
}
// update-begin--author:liaozhiyang---date:20240618---for【TV360X-932】评论加上换行
const lineFeed = (content) => {
return content.replace(/\n/g, '<br>');
};
// update-end--author:liaozhiyang---date:20240618---for【TV360X-932】评论加上换行
return {
dataList,
@ -303,6 +309,7 @@
bottomCommentRef,
visibleChange,
listRef,
lineFeed,
};
},
});
@ -319,6 +326,9 @@
.ant-comment {
width: 100%;
}
:deep(.ant-comment-avatar) {
cursor: default;
}
}
.comment-author {
span {

View File

@ -2,10 +2,10 @@
<div class="comment-tabs-warp" v-if="showStatus">
<a-tabs v-if="show" @change="handleChange" :animated="false">
<a-tab-pane v-if="showComment" tab="评论" key="comment" class="comment-list-tab">
<comment-list :tableName="tableName" :dataId="dataId" :datetime="datetime1" :otherHeight="otherHeight"></comment-list>
<comment-list :tableId="tableId" :tableName="tableName" :dataId="dataId" :datetime="datetime1" :otherHeight="otherHeight"></comment-list>
</a-tab-pane>
<a-tab-pane v-if="showFiles" tab="文件" key="file">
<comment-files :tableName="tableName" :dataId="dataId" :datetime="datetime2"></comment-files>
<comment-files :tableId="tableId" :tableName="tableName" :dataId="dataId" :datetime="datetime2"></comment-files>
</a-tab-pane>
<a-tab-pane v-if="showDataLog" tab="日志" key="log">
<data-log-list :tableName="tableName" :dataId="dataId" :datetime="datetime3"></data-log-list>
@ -33,6 +33,7 @@
DataLogList,
},
props: {
tableId: propTypes.string.def(''),
tableName: propTypes.string.def(''),
dataId: propTypes.string.def(''),
// 显示评论

View File

@ -1,7 +1,7 @@
<template>
<div :class="{'comment-active': commentActive}" class="comment-main" @click="handleClickBlank">
<textarea ref="commentRef" v-model="myComment" @keyup.enter="sendComment" @input="handleCommentChange" @blur="handleBlur" class="comment-content" :rows="3" placeholder="请输入你的评论,可以@成员" />
<div class="comment-content comment-html-shower" :class="{'no-content':noConent, 'top-div': showHtml, 'bottom-div': showHtml == false }" v-html="commentHtml" @click="handleClickHtmlShower"></div>
<textarea ref="commentRef" v-model="myComment" @keyup.enter="sendComment" @input="handleCommentChange" @blur="handleBlur" class="comment-content" :rows="3" placeholder="请输入你的评论,可以@成员"></textarea>
<div ref="commentContentRef" class="comment-content comment-html-shower" :class="{'no-content':noConent, 'top-div': showHtml, 'bottom-div': showHtml == false }" v-html="commentHtml" @click="handleClickHtmlShower"></div>
<div class="comment-buttons" v-if="commentActive">
<div style="cursor: pointer">
<Tooltip title="选择@用户">
@ -90,12 +90,18 @@
setup(props, { emit }) {
const uploadVisible = ref(false);
const uploadRef = ref();
const commentContentRef = ref<null | HTMLDivElement>(null);
//注册model
const [registerModal, { openModal, closeModal }] = useModal();
const buttonLoading = ref(false);
const myComment = ref<string>('');
function sendComment() {
console.log(myComment.value);
function sendComment(e) {
// update-begin--author:liaozhiyang---date:20240618---for【TV360X-932】评论加上换行
const keyCode = e.keyCode || e.which;
if (keyCode == 13 && e.shiftKey) {
return;
}
// update-end--author:liaozhiyang---date:20240618---for【TV360X-932】评论加上换行
let content = myComment.value;
if (!content && content !== '0') {
disabledButton.value = true;
@ -153,15 +159,29 @@
if (realname && username) {
let str = `${realname}[${username}]`;
let temp = myComment.value;
// update-begin--author:liaozhiyang---date:20240726---for【TV360X-929】选择@用户,应该插入到光标位置
if (!temp) {
myComment.value = '@' + str;
myComment.value = '@' + str + ' ';
} else {
if (temp.endsWith('@')) {
myComment.value = temp + str +' ';
const index = commentRef.value?.selectionStart ?? temp.length;
let startStr = temp.substring(0, index);
const endStr = temp.substring(index);
if (startStr.endsWith('@')) {
if (startStr.length >= 2) {
const i = startStr.length - 1;
const s_str = startStr.substring(0, i);
const e_str = startStr.substring(i);
const spacing = s_str.endsWith(' ') ? '' : ' ';
startStr = s_str + spacing + e_str;
}
myComment.value = startStr + str + ' ' + endStr;
} else {
myComment.value = '@' + str + ' ' + temp + ' ';
const _symbol = startStr && startStr.endsWith(' ') ? '@' : ' @';
myComment.value = startStr + _symbol + str + ' ' + endStr;
}
}
// update-begin--author:liaozhiyang---date:20240726---for【TV360X-929】选择@用户,应该插入到光标位置
//update-begin---author:wangshuai---date:2024-01-22---for:【QQYUN-8002】选完人鼠标应该放到后面并在前面加上空格---
showHtml.value = false;
commentRef.value.focus();
@ -171,18 +191,22 @@
}
closeModal();
}
function handleCommentChange() {
//console.log(1,e)
}
watch(
() => myComment.value,
(val) => {
if (val && val.endsWith('@')) {
openSelectUser();
}
// update-begin--author:liaozhiyang---date:20240724---for【TV360X-927】@只有在输入时弹出用户弹窗,删除时不应该弹出
function handleCommentChange(e) {
if (e.data === '@') {
e.target.blur();
openSelectUser();
}
);
}
// watch(
// () => myComment.value,
// (val) => {
// if (val && val.endsWith('@')) {
// openSelectUser();
// }
// }
// );
// update-end--author:liaozhiyang---date:20240724---for【TV360X-927】@只有在输入时弹出用户弹窗,删除时不应该弹出
const emojiButton = ref();
function onSelectEmoji(emoji) {
@ -255,6 +279,11 @@
}
function handleBlur() {
showHtml.value = true;
// update-begin--author:liaozhiyang---date:20240724---for解决多行获取焦点和失去焦点时滚动位置不一致
setTimeout(() => {
commentContentRef.value!.scrollTop = commentRef.value.scrollTop;
}, 0);
// update-end--author:liaozhiyang---date:20240724---for解决多行获取焦点和失去焦点时滚动位置不一致
}
const commentActive = ref(false);
@ -306,7 +335,8 @@
commentActive,
noConent,
changeActive,
selectFirstFile
selectFirstFile,
commentContentRef,
};
},
};
@ -342,7 +372,9 @@
width: 100%;
border: solid 0px;
outline: none;
// update-begin--author:liaozhiyang---date:20240724---forTV360X-933评论框拖动之后底部评论列表被覆盖了部分
resize: none;
// update-end--author:liaozhiyang---date:20240724---forTV360X-933评论框拖动之后底部评论列表被覆盖了部分
.emoji-item {
display: inline-block !important;
width: 0 !important;
@ -361,7 +393,10 @@
position: absolute;
top: 0;
left: 0;
height: 70px;
// update-begin--author:liaozhiyang---date:20240724---for解决多行获取焦点和失去焦点时滚动位置不一致
height: 78px;
overflow-y: auto;
// update-end--author:liaozhiyang---date:20240724---for解决多行获取焦点和失去焦点时滚动位置不一致
&.bottom-div {
z-index: -99;
}

View File

@ -124,6 +124,7 @@ export function useCommentWithFile(props) {
async function saveComment(obj) {
const {fromUserId, toUserId, commentId, commentContent} = obj;
let commentData = {
tableId: props.tableId,
tableName: props.tableName,
tableDataId: props.dataId,
fromUserId,