/* * @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)) : {}; };