/*
* @Author: wei.li
* @Date: 2021-12-01 09:18:50
* @LastEditors: levi7754 levi7754@163.com
* @LastEditTime: 2025-12-05 18:23:19
* @Description: file content
*/
// @ts-nocheck
import dayjs from 'dayjs';
import { isArray, isEmpty, findTree, isObject, isPlainObject } from 'xe-utils';
import type {
FormItemRenderOptions,
FormItemContentRenderParams,
VxeColumnPropTypes,
VxeGlobalRendererHandles
} from 'vxe-table';
import { cookies, storageLocal } from '@utogether/utils';
interface SuFormItemRenderOptions extends FormItemRenderOptions {
sourceData: IRecord;
}
interface SuEditRender extends VxeColumnPropTypes.EditRender {
sourceData: IRecord;
}
interface SuRenderCellOptions extends VxeGlobalRendererHandles.RenderCellOptions {
sourceData: IRecord[];
}
export const VxetableRender = (VXETable, { serviceApi, i18n }) => {
const dict = storageLocal.getItem('kLov');
// form render
VXETable.renderer.add('#SuSelect', {
renderTableEdit(renderOpts: VxeColumnPropTypes.EditRender, params) {
const { row, column } = params;
const { options, props, optionProps = {}, events } = renderOpts;
// @ts-ignore
const { label = 'label', value = 'value' } = optionProps;
const { loading = false, multiple } = props;
const componentHook = useComponent();
// 多选时
const modelField = multiple ? `_${column.field}` : column.field;
// 默认值
componentHook.setDefaultValue(props, row, column.field);
/**
* @description: 远程搜索对应的函数
* @param {*} val 录入的参数
*/
function remoteMethod(query) {
componentHook.remote(query, column.field, renderOpts);
}
/**
* @description: 选中值发生变化时触发
* @param {*} val 目前的选中值
*/
function onChange(val) {
componentHook.selectChange(val, row, column.field, renderOpts, {
column,
datasource: 'row'
});
}
function onInput(val) {
events?.input && events.input(params, val);
}
/**
* @description: 可清空的单选模式下用户点击清空按钮时触发
*/
function onClear() {
componentHook.clear(row, column.field, renderOpts, 'row');
}
function onFocus() {
!options.length && remoteMethod('');
events?.focus && events.focus(params, column.field);
}
return [
onFocus()}
onChange={v => onChange(v)}
onClear={() => onClear()}
onInput={v => onInput(v)}
>
{options.map(item => {
return ;
})}
];
},
// 可编辑显示模板
renderTableCell(renderOpts, params) {
const { row, column } = params;
const { props } = renderOpts;
const { textValue, multiple } = props;
let val = row[column.field];
if (textValue) {
val = eval(`row.${textValue}`) || row[column.field];
}
if (val && multiple && isArray(val)) {
val = val && val.join(',');
}
return [{val}];
},
renderItemContent(renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) {
const { data, field } = params;
const { options, props, optionProps = {}, events } = renderOpts;
// @ts-ignore
const { label = 'label', value = 'value' } = optionProps;
const { multiple, loading = false } = props;
const componentHook = useComponent();
// 多选时, model挂载到临时字段上
const modelField = multiple ? `_${field}` : field;
// 默认值
componentHook.setDefaultValue(props, data, field);
/**
* @description: 远程搜索对应的函数
* @param {*} val 录入的参数
*/
async function remoteMethod(query) {
componentHook.remote(query, field, renderOpts);
}
/**
* @description: 选中值发生变化时触发
* @param {*} val 目前的选中值
*/
function onChange(val) {
componentHook.selectChange(val, data, field, renderOpts, {
datasource: 'data'
});
}
function onInput(val) {
events?.input && events.input(params, val);
}
/**
* @description: 可清空的单选模式下用户点击清空按钮时触发
*/
function onClear() {
componentHook.clear(data, field, renderOpts, 'data');
}
function onFocus() {
!options.length && remoteMethod('');
events?.focus && events.focus(params);
}
return [
onFocus()}
onChange={v => onChange(v)}
onClear={() => onClear()}
onInput={() => onInput()}
>
{options.map(item => {
return ;
})}
];
}
});
// 时间范围
VXETable.renderer.add('#SuDateRange', {
renderItemContent(renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) {
const { data, field } = params;
const { props } = renderOpts;
if (props?.defaultValue && !data[field]) {
data[field] = props.defaultValue;
props.defaultValue = props.required ? props.defaultValue : null;
}
const end = new Date();
const shortcuts = [
{
text: i18n('message.udp.today', true),
value: () => [getTime(1, 'day'), end]
},
{
text: i18n('message.udp.yesterday', true),
value: () => [getTime(2, 'day'), getTime(2, 'day')]
},
{
text: i18n('message.udp.lastWeek', true),
value: () => [getTime(7, 'day'), end]
},
{
text: i18n('message.udp.lastMonth', true),
value: () => [getTime(1, 'month'), end]
},
{
text: i18n('message.udp.threeMonth', true),
value: () => [getTime(3, 'month'), end]
},
{
text: i18n('message.udp.halfYear', true),
value: () => [getTime(6, 'month'), end]
},
{
text: i18n('message.udp.anniversary', true),
value: () => [getTime(1, 'year'), end]
}
];
const getTime = (offset, type) => {
return dayjs().subtract(offset, type).add('1', 'day').format();
};
return [
];
}
});
// 下拉选择
VXETable.renderer.add('#select', {
renderTableEdit(renderOpts: VxeColumnPropTypes.EditRender, params) {
const { row, column } = params;
const { options, props, optionProps = {} } = renderOpts;
const { label = 'label', value = 'value' } = optionProps;
const { multiple } = props;
console.log('ssssss');
const componentHook = useComponent();
// 多选时
const modelField = multiple ? `_${column.field}` : column.field;
// 默认值
componentHook.setDefaultValue(props, row, column.field);
function onChange(val) {
componentHook.selectChange(val, row, column.field, renderOpts, {
column,
datasource: 'row'
});
}
function onClear() {
componentHook.clear(row, column.field, renderOpts, 'row');
}
return [
onChange(v)}
onClear={() => onClear()}
>
{options.map(item => {
return ;
})}
];
},
renderTableDefault(renderOpts: VxeColumnPropTypes.EditRender, params) {
const { row, column } = params;
const { options, props, optionProps = {} } = renderOpts;
const { label = 'label', value = 'value', extLabel } = optionProps;
const { multiple } = props;
console.log('defaultParams');
// 多选时
const modelField = multiple ? `_${column.field}` : column.field;
// 默认值
componentHook.setDefaultValue(props, row, column.field);
function onChange(val) {
componentHook.selectChange(val, row, column.field, renderOpts, {
column,
datasource: 'row'
});
}
function onClear() {
componentHook.clear(row, column.field, renderOpts, 'row');
}
return [
onChange(v)}
onClear={() => onClear()}
>
{options.map(item => {
return (
);
})}
];
},
// 可编辑显示模板
renderTableCell(renderOpts, params) {
const { row, column } = params;
const { props, options, optionProps } = renderOpts;
// @ts-ignore
const { label = 'label', value = 'value', extLabel } = optionProps;
console.log('defaultParams');
const { textValue } = props;
let val = row[column.field];
const item = options.find(opt => opt[value] === val);
if (textValue) {
val = eval(`row.${textValue}`) || row[column.field];
}
if (item) {
val = extLabel ? item[value] : item[label];
}
if (val && props?.multiple && isArray(val)) {
val = val && val.join(',');
}
return [{val}];
},
renderItemContent(renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) {
const { data, field } = params;
const { options, props, optionProps = {} } = renderOpts;
const { label = 'label', value = 'value' } = optionProps;
const { multiple } = props;
const componentHook = useComponent();
// 多选时, model挂载到临时字段上
const modelField = multiple ? `_${field}` : field;
// 默认值
multiple && componentHook.setDefaultValue(props, data, field);
/**
* @description: 选中值发生变化时触发
* @param {*} val 目前的选中值
*/
function onChange(val) {
componentHook.selectChange(val, data, field, renderOpts, {
datasource: 'data'
});
}
/**
* @description: 可清空的单选模式下用户点击清空按钮时触发
*/
function onClear() {
debugger;
componentHook.clear(data, field, renderOpts, 'data');
}
return [
onChange(v)}
onClear={() => onClear()}
>
{options.map(item => {
return ;
})}
];
}
});
// 下拉选择
VXETable.renderer.add('#textarea', {
renderTableEdit(renderOpts: VxeColumnPropTypes.EditRender, params) {
const { row, column } = params;
const { props } = renderOpts;
const componentHook = useComponent();
function onChange(val) {
componentHook.change(val, row, column.field, renderOpts, { column, datasource: 'row' });
}
function onClear() {
componentHook.clear(row, column.field, renderOpts, 'row');
}
return [
onChange(v)}
onClear={() => onClear()}
/>
];
},
renderTableDefault(renderOpts: VxeColumnPropTypes.EditRender, params) {
const { row, column } = params;
const { props } = renderOpts;
const componentHook = useComponent();
function onChange(val) {
componentHook.change(val, row, column.field, renderOpts, { column, datasource: 'row' });
}
function onClear() {
componentHook.clear(row, column.field, renderOpts, 'row');
}
return [
onChange(v)} onClear={() => onClear()} />
];
},
// 可编辑显示模板
renderTableCell(renderOpts, params) {
const { row, column } = params;
return row[column.field];
},
renderItemContent(renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) {
const { data, field } = params;
const { props } = renderOpts;
const componentHook = useComponent();
// 默认值
const { defaultValue } = props;
if (defaultValue) {
data[field] = defaultValue;
}
/**
* @description: 选中值发生变化时触发
* @param {*} val 目前的选中值
*/
function onChange(val) {
componentHook.change(val, data, field, renderOpts, { datasource: 'data' });
}
/**
* @description: 可清空的单选模式下用户点击清空按钮时触发
*/
function onClear() {
componentHook.clear(data, field, renderOpts, 'data');
}
return [ onChange(v)} onClear={() => onClear()} />];
}
});
// Lov
VXETable.renderer.add('#lov', {
renderItemContent(renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) {
const { data, field } = params;
const { props, events } = renderOpts;
const componentHook = useComponent();
// 默认值
const { defaultValues } = props;
if (defaultValues) {
data[field] = defaultValues;
}
/**
* @description: 选中值发生变化时触发
* @param {*} val 目前的选中值
*/
function onChange(record, displayField) {
const { mapField, field, displayName, multiple } = props;
if (!multiple) {
const obj = {
[displayName || field]: record[displayField || displayName || field]
};
for (const key in mapField) {
obj[key] = record[mapField[key]];
}
Object.assign(data, obj);
} else if (multiple) {
const obj = {};
data[displayName || field] = record.reduce((curv, prev) => {
for (const key in mapField) {
obj[key] = !curv ? prev[mapField[key]] : obj[key] + ',' + prev[mapField[key]];
}
curv = !curv ? prev[displayName || field] : curv + ',' + prev[displayName || field];
return curv;
}, '');
Object.assign(data, obj);
}
const parm = { data, options: record, field };
events?.change && events.change(parm);
}
/**
* @description: 点击清空按钮时触发
*/
function onClear() {
componentHook.clear(data, field, renderOpts, 'data');
}
return [
onChange(v, f)} onClear={() => onClear()} />
];
},
// 单元格编辑
renderTableEdit(renderOpts: VxeColumnPropTypes.EditRender, params) {
const { row, column } = params;
const { props, events } = renderOpts;
const componentHook = useComponent();
console.log('lov====');
function onChange(record, displayField) {
const { mapField, field, displayName, multiple } = props;
if (!multiple) {
const obj = {
[displayName || field]: record[displayField || displayName || field]
};
for (const key in mapField) {
obj[key] = record[mapField[key]];
}
Object.assign(row, obj);
}
const params = { row, options: record, field: column.field, column };
events?.change && events.change(params);
}
function onClear() {
componentHook.clear(row, field, renderOpts, 'row');
}
return [
onChange(v, f)}
onClear={() => onClear()}
/>
];
},
// 单元格默认
renderTableDefault(renderOpts: VxeColumnPropTypes.EditRender, params) {
const { row, column } = params;
const { props, events } = renderOpts;
const componentHook = useComponent();
function onChange(record, displayField) {
const { mapField, field, displayName, multiple } = props;
if (!multiple) {
const obj = {
[displayName || field]: record[displayField || displayName || field]
};
for (const key in mapField) {
obj[key] = record[mapField[key]];
}
Object.assign(row, obj);
}
const params = { row, options: record, field: column.field, column };
events?.change && events.change(params);
}
/**
* @description 清空数据
*/
function onClear() {
componentHook.clear(row, field, renderOpts, 'row');
}
return [
onChange(v, f)} onClear={() => onClear()} />
];
},
// 可编辑显示模板
renderTableCell(renderOpts, params) {
const { row, column } = params;
return [{row[column.field]}];
}
});
// form upload
VXETable.renderer.add('#upload', {
renderTableEdit(renderOpts: VxeColumnPropTypes.EditRender, params) {
const { row, column } = params;
const { props, events } = renderOpts;
/**
* @description: 点击清空按钮时触发
*/
function onClear(file, fielist) {
row[column.field] = '';
events?.remove && events?.remove(file, fielist);
}
return [
onClear(file, fielist)}
/>
];
},
// 可编辑显示模板
renderTableCell(renderOpts, params) {
const { row, column } = params;
const { props } = renderOpts;
return [
];
},
renderItemContent(renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) {
const { data, field } = params;
const { props, events } = renderOpts;
/**
* @description: 点击清空按钮时触发
*/
function onClear(file, fielist) {
data[field] = '';
events?.remove && events?.remove(file, fielist);
}
if (props.singleMode) {
// 单个模式下,限制只能单个上传
Object.assign(props, { multiple: false, limitCount: 1, autoHiddenButton: true });
}
if (!props.mode || props.mode === 'image') {
props.imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'apk'];
}
console.log({ props });
return [
onClear(file, fielist)}
/>
];
}
});
// 创建一个简单的超链接渲染
VXETable.renderer.add('#tag', {
// 默认显示模板
renderTableDefault(renderOpts, params) {
const { row, column } = params;
const {
props: { code, tagMap }
} = renderOpts;
function getTagType() {
return row[column.field] ? tagMap[row[column.field]] : null;
}
return [
row[column.field] ? (
{getValue(code, row[column.field])}
) : null
];
}
});
VXETable.renderer.add('#switch', {
renderTableDefault(renderOpts, params) {
const { row, column } = params;
const {
props: { code, activeValue, inactiveValue },
events
} = renderOpts;
function onChange(value) {
const params = { row, column, value };
events?.change && events.change(params);
}
return [
row[column.field] ? (
onChange(v)}
/>
) : null
];
}
});
VXETable.renderer.add('#iconSelect', {
renderTableEdit(renderOpts: VxeColumnPropTypes.EditRender, params) {
const { row, column } = params;
return [];
},
renderTableCell(renderOpts, params) {
const { row, column } = params;
return [];
},
renderItemContent(renderOpts: FormItemRenderOptions, params: FormItemContentRenderParams) {
const { data, field } = params;
return [];
}
});
VXETable.renderer.add('#treeSelect', {
renderTableEdit(renderOpts: SuEditRender, params) {
const { row, column } = params;
const { props, sourceData, events } = renderOpts;
return [
onCheckChange(node, selected, isLeaf, events)}
onNodeClick={(data, node, treeNode) => onNodeClick(data, node, treeNode, events)}
onCurrentChange={(data, node) => onCurrentChange(data, node, events)}
/>
];
},
renderTableCell(renderOpts: SuRenderCellOptions, params) {
const { row, column } = params;
const { sourceData, props = {} } = renderOpts;
const { children = 'children', label = 'label' } = props;
const data = findTree(sourceData, item => item['value'] === row[column.field], { children });
return data ? [{data.item[label]}] : null;
},
renderItemContent(renderOpts: SuFormItemRenderOptions, params: FormItemContentRenderParams) {
const { data, field } = params;
const { props, sourceData, events } = renderOpts;
return [
onCheckChange(node, selected, isLeaf, events)}
onNodeClick={(data, node, treeNode) => onNodeClick(data, node, treeNode, events)}
onCurrentChange={(data, node) => onCurrentChange(data, node, events)}
/>
];
}
});
/**
* 组件公共方法
* @returns
*/
const useComponent = () => {
// 下拉框选择
/**
* select下拉选择change事件
* @param value 选择值
* @param data 数据源
* @param field 字段
* @param renderOpts 渲染参数
* @param opts 数据源类型等
* @return `{ data: 数据源, field: 字段, options?: 选中的选项组, option?: 选中的选项, column?: 列信息 }`
*/
const selectChange = (value, data, field, renderOpts, opts) => {
let option = null;
const { datasource } = opts;
const { options, props, optionProps = {}, events } = renderOpts;
const callData = { [datasource]: data, field };
// 1、清空原有数据
data[field] = null;
if (!isEmpty(value) && props?.multiple) {
// 2、给实际字段赋值
data[field] = value.join(',');
option = [];
// 多选
value.forEach(item => {
const selectOpt = options.find(opt => item === opt[optionProps?.value]);
if (selectOpt && !isEmpty(props.mapField) && isObject(props.mapField)) {
Object.keys(props.mapField).forEach(key => {
const val = selectOpt[props.mapField[key]];
const ov = data[key] ? data[key].toString() : '';
// 默认用逗号(,)分割
data[key] = ov && !ov.includes(val) ? `${ov},${val}` : val;
});
}
selectOpt && option.push(selectOpt);
});
Object.assign(callData, { options: option });
} else {
// 3、原字段赋值, 多选进来说明必定是空值
data[field] = props?.multiple ? null : value;
option = options.find(opt => value === opt[optionProps?.value]);
if (!isEmpty(props.mapField) && isObject(props.mapField)) {
Object.keys(props.mapField).forEach(key => {
data[key] = option ? option[props.mapField[key]] : null;
});
}
Object.assign(callData, { option });
}
datasource === 'row' && Object.assign(callData, { column: opts.column });
events?.change && events.change(callData);
};
const setDefaultValue = (props, data, field) => {
const { multiple, defaultValue } = props;
// 多选时, model挂载到临时字段上
const modelField = multiple ? `_${field}` : field;
// 默认值
if (!data[field] && defaultValue) {
data[field] = defaultValue;
if (isPlainObject(defaultValue)) {
const keys = Object.keys(defaultValue);
keys.forEach(key => {
data[key] = data[key] || defaultValue[key];
});
}
}
if (!data[modelField] && data[field]) {
data[modelField] = multiple ? data[field]?.split(',') : data[field];
}
};
const remote = async (query, field, renderOpts) => {
const { options, props, events } = renderOpts;
const { fetchField, url, defaultParams = {}, method = 'get' } = props;
const commonParam = getCookieParam();
const params = Object.assign(
{ pageSize: 20, pageNum: 1, ...commonParam, ...defaultParams },
{ [fetchField || field]: query }
);
if (props.loading) return;
let res;
try {
options.length = 0;
props.loading = true;
res = await serviceApi[method](url, params);
if (events?.filterMethod) {
options.push(...events.filterMethod(res.list || res));
} else if (res) {
res.list ? options.push(...res.list) : options.push(...res);
}
} finally {
props.loading = false;
}
};
// 清除数据
const clear = (data, field, renderOpts, datasource) => {
const { props, events } = renderOpts;
debugger;
const { mapField } = props;
data[field] = null;
if (props?.multiple) {
data[`_${field}`] = [];
}
if (!isEmpty(mapField)) {
for (const key in mapField) {
data[key] = null;
}
}
events?.clear && events.clear({ [datasource]: data, field });
};
const change = (data, field, mapField, events, datasource) => {
data[field] = null;
if (!isEmpty(mapField)) {
for (const key in mapField) {
data[key] = null;
}
}
events?.change && events.change({ [datasource]: data, field });
};
return { clear, change, remote, selectChange, setDefaultValue };
};
/**
* 当复选框被点击的时候触发
* @param node 传递给 data 属性的数组中该节点所对应的对象
* @param selected 节点本身是否被选中
* @param isLeaf 节点的子树中是否有被选中的节点
*/
const onCheckChange = (node, selected, isLeaf, events) => {
console.log(node, selected, isLeaf);
events?.checkChange && events?.checkChange(node, selected, isLeaf);
};
/**
* 节点被点击的时候触发
* @param nodeObject 点击的节点对象
* @param node TreeNode 的 node 属性
* @param treeNode TreeNode
*/
const onNodeClick = (nodeObject, node, treeNode, events) => {
events?.checkChange && events?.checkChange(nodeObject, node, treeNode);
};
/**
* 当前选中节点变化时触发的事件
* @param data 当前节点的数据
* @param node 当前节点的 Node 对象
*/
const onCurrentChange = (data, node, events) => {
events?.checkChange && events?.checkChange(data, node);
};
function getValue(code, value) {
if (!value || !code || !dict) return value;
return dict[code]?.children?.find(item => item.dictCode === value)?.dictName;
}
return VXETable;
};
const getCookieParam = () => {
const kCOOKIES = 'kCookies_param';
return cookies.get(kCOOKIES) ? JSON.parse(cookies.get(kCOOKIES)) : {};
};