11 changed files with 2258 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||
<template> |
|||
<div> |
|||
<LuckySheet></LuckySheet> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import LuckySheet from './luckysheet/LuckySheet.vue'; |
|||
</script> |
|||
|
|||
<style scoped></style> |
@ -0,0 +1,358 @@ |
|||
<template> |
|||
<w-dialog ref="dialogRef" :title="state.dialogTitle" :maximized="true" :buttons="dialog.buttons" body-padding="0px 0px 0px 0px"> |
|||
<div class="flex justify-end gap-2 p-1.5"> |
|||
<q-file v-model="impFile" label="导入Excel文件" input-class="w-[400px]" filled dense accept=".xlsx,.xls" @update:model-value="loadExcel($event)"> |
|||
<template #append> |
|||
<q-icon name="attachment" /> |
|||
</template> |
|||
</q-file> |
|||
<div class="flex flex-none"> |
|||
<q-btn label="选取变量" :icon="PlatformIconEnum.选取变量" @click="selectParams"></q-btn> |
|||
</div> |
|||
<div class="flex flex-none"> |
|||
<q-btn-dropdown :icon="PlatformIconEnum.设置" label="循环属性配置" unelevated outline> |
|||
<q-list> |
|||
<q-item v-close-popup clickable> |
|||
<q-item-section> |
|||
<q-item-label @click="openConfigDialog('main')"><q-icon :name="PlatformIconEnum.更多查询"></q-icon> 整体配置(首列)</q-item-label> |
|||
</q-item-section> |
|||
</q-item> |
|||
<q-item v-close-popup clickable> |
|||
<q-item-section> |
|||
<q-item-label @click="openConfigDialog('column')"><q-icon :name="PlatformIconEnum.更多查询"></q-icon> 列配置(非首列)</q-item-label> |
|||
</q-item-section> |
|||
</q-item> |
|||
</q-list> |
|||
</q-btn-dropdown> |
|||
</div> |
|||
<TemplateParamsSelectDialog ref="paramsSelectDialogRef" @set-params="setParams"></TemplateParamsSelectDialog> |
|||
<TemplateSetForeachPropsDialog ref="setForeachPropsDialogRef" @set-for-each-params="setForEachParams"></TemplateSetForeachPropsDialog> |
|||
<TemplateSetColumnParamsDialog ref="setColumnParamsDialogRef" @set-for-each-params="setForEachParams"></TemplateSetColumnParamsDialog> |
|||
</div> |
|||
<div id="luckysheet"></div> |
|||
</w-dialog> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, reactive, nextTick, onBeforeMount } from 'vue'; |
|||
import LuckyExcel from 'luckyexcel'; |
|||
import { exportExcel } from './luckysheet/exportExcel'; |
|||
import { useQuasar } from 'quasar'; |
|||
import { axios, Environment, NotifyManager, PlatformIconEnum, Tools } from 'platform-core'; |
|||
|
|||
import TemplateParamsSelectDialog from './TemplateParamsSelectDialog.vue'; |
|||
import TemplateSetForeachPropsDialog from './TemplateSetForeachPropsDialog.vue'; |
|||
import TemplateSetColumnParamsDialog from './TemplateSetColumnParamsDialog.vue'; |
|||
|
|||
const $q = useQuasar(); |
|||
|
|||
const dialogRef = ref(); |
|||
const state = reactive({ |
|||
dialogTitle: '模板制定', |
|||
dataId: '', |
|||
}); |
|||
const dialog = { |
|||
buttons: [ |
|||
{ |
|||
icon: PlatformIconEnum.保存, |
|||
label: '保存', |
|||
click: () => { |
|||
save(); |
|||
}, |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
const show = async (tableSelectedRow: any) => { |
|||
state.dialogTitle = '模板制定——' + tableSelectedRow.templateName + '(' + tableSelectedRow.templateCode + ')'; |
|||
state.dataId = tableSelectedRow.id; |
|||
dialogRef.value.show(); |
|||
nextTick(async () => { |
|||
if (window.luckysheet) { |
|||
window.luckysheet.destroy(); |
|||
} |
|||
const resp = await axios.get(Environment.apiContextPath('api/excel/template/') + state.dataId); |
|||
if (resp?.data?.templateConfig) { |
|||
luckysheetOptions.data = [eval('(' + resp.data.templateConfig + ')')]; |
|||
} |
|||
// eslint-disable-next-line |
|||
luckysheet.create(luckysheetOptions); |
|||
}); |
|||
}; |
|||
const hide = () => { |
|||
dialogRef.value.hide(); |
|||
}; |
|||
|
|||
const impFile = ref(null); |
|||
const paramsSelectDialogRef = ref(); |
|||
const setForeachPropsDialogRef = ref(); |
|||
const setColumnParamsDialogRef = ref(); |
|||
|
|||
const loadExcel = (value) => { |
|||
const { name } = value; |
|||
const suffixArr = name.split('.'); |
|||
const suffix = suffixArr[suffixArr.length - 1]; |
|||
if (suffix !== 'xlsx') { |
|||
NotifyManager.warn('只能导入 .xlsx 格式的Excel文件'); |
|||
return; |
|||
} |
|||
LuckyExcel.transformExcelToLucky(value, (exportJson) => { |
|||
if (exportJson.sheets === null || exportJson.sheets.length === 0) { |
|||
NotifyManager.warn('导入的Excel为空文件'); |
|||
return; |
|||
} |
|||
|
|||
window.luckysheet.destroy(); |
|||
|
|||
const data = [ |
|||
{ |
|||
...exportJson.sheets[0], |
|||
...{ |
|||
column: 26, |
|||
row: 30, |
|||
}, |
|||
}, |
|||
]; |
|||
luckysheetOptions.data = data; |
|||
window.luckysheet.create({ |
|||
lang: 'zh', |
|||
container: 'luckysheet', |
|||
showinfobar: false, |
|||
showtoolbar: true, // 是否显示工具栏,设置字体,边框,插入这些工具按钮。 |
|||
sheetFormulaBar: true, // 是否显示公式栏 |
|||
showsheetbar: false, // 是否显示底部sheet页按钮 |
|||
data: data, |
|||
title: exportJson.info.name, |
|||
userInfo: exportJson.info.name.creator, |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const downloadExcel = () => { |
|||
// eslint-disable-next-line |
|||
exportExcel(luckysheet.getAllSheets(), '下载'); |
|||
}; |
|||
|
|||
const luckysheetData = [ |
|||
{ |
|||
name: 'Cell', // 工作表名称 |
|||
color: '', // 工作表颜色 |
|||
index: 0, // 工作表索引 |
|||
status: 1, // 激活状态 |
|||
order: 0, // 工作表的下标 |
|||
hide: 0, // 是否隐藏 |
|||
defaultRowHeight: 19, // 自定义行高 |
|||
defaultColWidth: 73, // 自定义列宽 |
|||
column: 26, |
|||
row: 30, |
|||
celldata: [], // 初始化使用的单元格数据 |
|||
config: { |
|||
merge: {}, // 合并单元格 |
|||
rowlen: {}, // 表格行高 |
|||
columnlen: {}, // 表格列宽 |
|||
rowhidden: {}, // 隐藏行 |
|||
colhidden: {}, // 隐藏列 |
|||
borderInfo: [], // 边框 |
|||
authority: {}, // 工作表保护 |
|||
}, |
|||
scrollLeft: 0, // 左右滚动条位置 |
|||
scrollTop: 0, // 上下滚动条位置 |
|||
luckysheet_select_save: [], // 选中的区域 |
|||
calcChain: [], // 公式链 |
|||
isPivotTable: false, // 是否数据透视表 |
|||
pivotTable: {}, // 数据透视表设置 |
|||
filter_select: {}, // 筛选范围 |
|||
filter: null, // 筛选配置 |
|||
luckysheet_alternateformat_save: [], // 交替颜色 |
|||
luckysheet_alternateformat_save_modelCustom: [], // 自定义交替颜色 |
|||
luckysheet_conditionformat_save: {}, // 条件格式 |
|||
frozen: {}, // 冻结行列配置 |
|||
chart: [], // 图表配置 |
|||
zoomRatio: 1, // 缩放比例 |
|||
image: [], // 图片 |
|||
showGridLines: 1, // 是否显示网格线 |
|||
dataVerification: {}, // 数据验证配置 |
|||
}, |
|||
]; |
|||
const luckysheetOptions = { |
|||
container: 'luckysheet', // 设定DOM容器的id |
|||
lang: 'zh', // 设定表格语言 |
|||
title: '测试', // 设定表格名称 |
|||
allowEdit: true, // 是否允许编辑 |
|||
rowHeaderWidth: 46, // 行标题区域的宽度,如果设置为0,则表示隐藏行标题,也就是表格第一行,A,B,C这行,默认值46 |
|||
columnHeaderHeight: 20, // 列标题区域的高度,如果设置为0,则表示隐藏列标题,也就是序号这一行,默认值20 |
|||
showtoolbar: true, // 是否显示工具栏,设置字体,边框,插入这些工具按钮。 |
|||
showinfobar: false, // 是否显示顶部信息栏,luckysheet的logo,表格名称这一栏。 |
|||
userInfo: 'likunming', // 右上角用户信息,若需要展示,必须配合showinfobar使用 |
|||
sheetFormulaBar: true, // 是否显示公式栏 |
|||
defaultFontSize: 11, // 初始化默认字体大小,默认11 |
|||
showsheetbar: false, // 是否显示底部sheet页按钮 |
|||
data: luckysheetData, // 表格数据 |
|||
}; |
|||
const selectParams = () => { |
|||
// eslint-disable-next-line |
|||
const selectRange = luckysheet.getRange(); |
|||
if (!selectRange || selectRange.length === 0) { |
|||
NotifyManager.warn('请选择要插入变量的Excel单元格'); |
|||
return; |
|||
} else if (selectRange && selectRange.length > 1) { |
|||
NotifyManager.warn('只支持单个选区选取变量'); |
|||
return; |
|||
} |
|||
paramsSelectDialogRef.value.show(state.dataId); |
|||
}; |
|||
const setParams = (paramsCode: string | Array<string>) => { |
|||
if (typeof paramsCode === 'string') { |
|||
// eslint-disable-next-line |
|||
let rangeValue = luckysheet.getRangeValue(); |
|||
rangeValue[0][0] = { ...rangeValue[0][0], ...{ m: paramsCode, v: paramsCode } }; |
|||
// eslint-disable-next-line |
|||
luckysheet.setRangeValue(rangeValue); |
|||
} else { |
|||
// eslint-disable-next-line |
|||
const currRange = luckysheet.getRange(); |
|||
paramsCode.forEach((item, index) => { |
|||
const tmpRangeValue = [[{ m: item, v: item }]]; |
|||
const tmp = { |
|||
row: [currRange[0].row[0], currRange[0].row[1]], |
|||
column: [currRange[0].column[0] + index, currRange[0].column[1] + index], |
|||
}; |
|||
// eslint-disable-next-line |
|||
luckysheet.setRangeValue(tmpRangeValue, { range: tmp }); |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
// eslint-disable-next-line |
|||
const psReg = /\@\{(.*)\}\@/; |
|||
const openConfigDialog = (configType) => { |
|||
// eslint-disable-next-line |
|||
const range = luckysheet.getRange(); |
|||
// eslint-disable-next-line |
|||
const rangeValue = luckysheet.getRangeValue(); |
|||
if (!range) { |
|||
NotifyManager.info('请选择要配置属性的单元格'); |
|||
return; |
|||
} else if (range && range.length > 1) { |
|||
NotifyManager.warn('多个选区无法配置属性,选择单元格既可'); |
|||
return; |
|||
} |
|||
let data = {}; |
|||
if (rangeValue[0] && rangeValue[0][0] && rangeValue[0][0].ps && rangeValue[0][0].ps.value) { |
|||
let tmp = rangeValue[0][0].ps.value.replaceAll('\n', ''); |
|||
data = JSON.parse(psReg.exec(tmp)[1]); |
|||
} |
|||
if (configType === 'main') { |
|||
setForeachPropsDialogRef.value.show(state.dataId, data); |
|||
} else { |
|||
setColumnParamsDialogRef.value.show(data); |
|||
} |
|||
}; |
|||
|
|||
const setForEachParams = (formData) => { |
|||
// eslint-disable-next-line |
|||
const range = luckysheet.getRange(); |
|||
let tmp = '@{{\n'; |
|||
Object.keys(formData).forEach((item) => { |
|||
if (!Tools.isEmpty(formData[item])) { |
|||
tmp += '"' + item + '"' + ':' + '"' + formData[item] + '"' + ',' + '\n'; |
|||
} |
|||
}); |
|||
tmp = tmp.substring(0, tmp.length - 2); |
|||
tmp += '\n}}@'; |
|||
// eslint-disable-next-line |
|||
luckysheet.insertComment(range[0].row[0], range[0].column[0], tmp); |
|||
// 插入批注后调整批注框大小 |
|||
luckysheetOptions.data[0].data[range[0].row[0]][range[0].column[0]].ps.width = 300; |
|||
luckysheetOptions.data[0].data[range[0].row[0]][range[0].column[0]].ps.height = 150; |
|||
luckysheetOptions.data[0].data[range[0].row[0]][range[0].column[0]].ps.isShow = false; |
|||
// eslint-disable-next-line |
|||
luckysheet.refresh(); |
|||
}; |
|||
const save = async () => { |
|||
// eslint-disable-next-line |
|||
if (!luckysheet.getAllSheets() || luckysheet.getAllSheets().length === 0) { |
|||
NotifyManager.warn('系统异常,未获取到需要保存的Sheet数据'); |
|||
return; |
|||
} |
|||
// 覆盖celldata数据缩减传给后端的数据量,该数据仅用以初始化表格,无需保存,初始化时可使用data属性。 |
|||
// eslint-disable-next-line |
|||
const saveData = { ...luckysheet.getAllSheets()[0], ...{ celldata: [] } }; |
|||
// 保存 |
|||
let requestParams = { |
|||
method: 'POST', |
|||
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
|||
data: { templateConfig: JSON.stringify(saveData) }, |
|||
url: Environment.apiContextPath('api/excel/template/updateConfig/') + state.dataId, |
|||
}; |
|||
const resp = await axios(requestParams).catch((error) => { |
|||
console.info('error====', error); |
|||
NotifyManager.error('保存失败'); |
|||
}); |
|||
NotifyManager.info('保存成功'); |
|||
// dialogRef.value.dialogHide(); |
|||
}; |
|||
|
|||
const buttons = ref([ |
|||
{ |
|||
name: 'selectParams', |
|||
icon: 'find_in_page', |
|||
label: '选取变量', |
|||
click: selectParams, |
|||
}, |
|||
[ |
|||
{ |
|||
name: 'foreachPropsConfig', |
|||
icon: 'settings', |
|||
label: '循环属性配置', |
|||
}, |
|||
{ |
|||
name: 'mainConfig', |
|||
icon: 'build_circle', |
|||
label: '整体配置(首列)', |
|||
click: () => { |
|||
openConfigDialog('main'); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'columnConfig', |
|||
icon: 'build_circle', |
|||
label: '列配置(非首列)', |
|||
click: () => { |
|||
openConfigDialog('column'); |
|||
}, |
|||
}, |
|||
], |
|||
{ |
|||
name: 'download', |
|||
icon: 'download', |
|||
label: '导出文件', |
|||
click: downloadExcel, |
|||
}, |
|||
]); |
|||
|
|||
onBeforeMount(() => { |
|||
window.setInputZIndex = () => { |
|||
return true; |
|||
}; |
|||
window.setRightClickMenuZIndex = () => { |
|||
return true; |
|||
}; |
|||
}); |
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped> |
|||
#luckysheet { |
|||
margin: 0px; |
|||
padding: 0px; |
|||
position: absolute; |
|||
width: 100%; |
|||
left: 0px; |
|||
top: 50px; |
|||
bottom: 0px; |
|||
} |
|||
</style> |
@ -0,0 +1,131 @@ |
|||
<template> |
|||
<div style="height: 100%"> |
|||
<w-grid |
|||
ref="templateGridRef" |
|||
title="Excel模板列表" |
|||
sort-no |
|||
:data-url="Environment.apiContextPath('api/excel/template')" |
|||
:query-form-cols-num="2" |
|||
:query-form-fields="templateGrid.queryFormFields" |
|||
:toolbar-actions="templateGrid.toolbarActions" |
|||
:columns="templateGrid.columns" |
|||
:editor="templateGrid.editor" |
|||
:viewer="templateGrid.viewer" |
|||
:sort-by="['-lastModifyDate']" |
|||
></w-grid> |
|||
<TemplateParamsDialog ref="templateParamsDialogRef"></TemplateParamsDialog> |
|||
<TemplateConfigDialog ref="templateConfigDialogRef"></TemplateConfigDialog> |
|||
<TemplateReportDialog ref="templateReportDialogRef"></TemplateReportDialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
import { Environment } from 'platform-core'; |
|||
|
|||
import TemplateParamsDialog from './TemplateParamsDialog.vue'; |
|||
import TemplateConfigDialog from './TemplateConfigDialog.vue'; |
|||
import TemplateReportDialog from './TemplateReportDialog.vue'; |
|||
|
|||
const templateGridRef = ref(); |
|||
const templateParamsDialogRef = ref(); |
|||
const templateConfigDialogRef = ref(); |
|||
const templateReportDialogRef = ref(); |
|||
|
|||
const templateGrid = { |
|||
queryFormFields: [ |
|||
{ label: '模板编码', name: 'templateCode', type: 'w-text' }, |
|||
{ label: '模板名称', name: 'templateName', type: 'w-text' }, |
|||
], |
|||
toolbarActions: [ |
|||
'query', |
|||
'reset', |
|||
'separator', |
|||
[ |
|||
{ |
|||
name: 'operate', |
|||
icon: 'difference', |
|||
label: '操作', |
|||
}, |
|||
'add', |
|||
{ |
|||
extend: 'edit', |
|||
afterClick: (args) => { |
|||
args.grid.setEditDataUrl(Environment.apiContextPath('api/excel/template/updateField')); |
|||
}, |
|||
}, |
|||
'remove', |
|||
], |
|||
{ |
|||
name: 'params', |
|||
icon: 'settings', |
|||
label: '变量维护', |
|||
click: (args) => { |
|||
templateParamsDialogRef.value.show(args.selecteds); |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'templateConfig', |
|||
icon: 'build_circle', |
|||
label: '模板制定', |
|||
enableIf: (args) => { |
|||
if (args.selected) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}, |
|||
click: (args) => { |
|||
templateConfigDialogRef.value.show(args.selected); |
|||
}, |
|||
}, |
|||
'separator', |
|||
{ |
|||
name: 'reportView', |
|||
icon: 'visibility', |
|||
label: '报表查看', |
|||
enableIf: (args) => { |
|||
if (args.selected) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}, |
|||
click: (args) => { |
|||
templateReportDialogRef.value.show(args.selected); |
|||
}, |
|||
}, |
|||
'separator', |
|||
], |
|||
columns: [ |
|||
{ name: 'templateCode', label: '模板编码' }, |
|||
{ name: 'templateName', label: '模板名称' }, |
|||
{ name: 'lastModifier', label: '最后修改人' }, |
|||
{ name: 'lastModifyDate', label: '最后修改时间' }, |
|||
], |
|||
editor: { |
|||
dialog: { |
|||
width: '50%', |
|||
height: '30%', |
|||
}, |
|||
form: { |
|||
colsNum: 1, |
|||
fields: [ |
|||
{ label: '模板编码', name: 'templateCode', type: 'w-text', requiredIf: true }, |
|||
{ label: '模板名称', name: 'templateName', type: 'w-text', requiredIf: true }, |
|||
], |
|||
}, |
|||
}, |
|||
viewer: { |
|||
panel: { |
|||
fields: [ |
|||
{ name: 'id', label: '模板ID' }, |
|||
{ name: 'templateCode', label: '模板编码' }, |
|||
{ name: 'templateName', label: '模板名称' }, |
|||
{ name: 'lastModifier', label: '最后修改人' }, |
|||
{ name: 'lastModifyDate', label: '最后修改时间' }, |
|||
], |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -0,0 +1,464 @@ |
|||
<template> |
|||
<w-dialog ref="dialogRef" title="变量维护" :maximized="true"> |
|||
<w-grid |
|||
ref="templateParamsGridRef" |
|||
title="模板变量列表" |
|||
:auto-fetch-data="false" |
|||
:data-url="Environment.apiContextPath('api/excel/template/params')" |
|||
:sort-no="true" |
|||
:columns="templateParamsGrid.columns" |
|||
:toolbar-actions="templateParamsGrid.toolbarActions" |
|||
:query-form-cols-num="4" |
|||
:query-form-fields="templateParamsGrid.queryFormFields" |
|||
:pagination="{ |
|||
sortBy: 'lastModifyDate', |
|||
descending: true, |
|||
}" |
|||
></w-grid> |
|||
<w-dialog ref="templateParamsDialogRef" :title="state.templateParamsDialogTitle" width="80%" height="80%" :buttons="templateParamsDialog.buttons"> |
|||
<w-form ref="templateParamsAddEditFormRef" :cols-num="3" :fields="templateParamsDialog.formFields" class="p-1.5"> </w-form> |
|||
<w-grid |
|||
v-show="state.paramsType && state.paramsType === 'LIST'" |
|||
ref="templateParamsListFieldGridRef" |
|||
title="字段列表" |
|||
dense |
|||
:draggable="true" |
|||
:pageable="false" |
|||
:config-button="false" |
|||
:auto-fetch-data="false" |
|||
:fetch-data-url="Environment.apiContextPath('api/excel/template/params/list?sortBy=sortNo')" |
|||
:toolbar-actions="templateParamsListFieldGrid.toolbarActions" |
|||
:columns="templateParamsListFieldGrid.columns" |
|||
></w-grid> |
|||
<w-dialog |
|||
ref="templateParamsListFieldDialogRef" |
|||
:title="state.templateParamsListFieldDialogTitle" |
|||
width="60%" |
|||
height="50%" |
|||
:buttons="templateParamsListFieldDialog.buttons" |
|||
> |
|||
<w-form ref="templateParamsListFieldAddEditFormRef" :cols-num="1" :fields="templateParamsListFieldDialog.formFields" class="p-1.5"> </w-form> |
|||
</w-dialog> |
|||
</w-dialog> |
|||
</w-dialog> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, reactive, nextTick } from 'vue'; |
|||
import { axios, Environment, NotifyManager } from 'platform-core'; |
|||
|
|||
const dialogRef = ref(); |
|||
const templateParamsGridRef = ref(); |
|||
const templateParamsDialogRef = ref(); |
|||
const templateParamsAddEditFormRef = ref(); |
|||
const templateParamsListFieldGridRef = ref(); |
|||
const templateParamsListFieldDialogRef = ref(); |
|||
const templateParamsListFieldAddEditFormRef = ref(); |
|||
|
|||
const state = reactive({ |
|||
templateArray: [], |
|||
tableSelectedTemplateId: null, |
|||
paramsType: null, |
|||
templateParamsDialogTitle: '新增变量', |
|||
templateParamsListFieldDialogTitle: '新增字段', |
|||
}); |
|||
|
|||
const templateParamsGrid = { |
|||
queryFormFields: [ |
|||
{ label: '变量编码', name: 'paramsCode', type: 'w-text' }, |
|||
{ label: '变量名称', name: 'paramsName', type: 'w-text' }, |
|||
{ |
|||
label: '变量类型', |
|||
name: 'paramsType', |
|||
type: 'w-select', |
|||
queryOperator: 'equals', |
|||
options: [ |
|||
{ label: '单值', value: 'SING' }, |
|||
{ label: '列表', value: 'LIST' }, |
|||
], |
|||
}, |
|||
{ |
|||
label: '所属模板', |
|||
name: 'reportExcelTemplate', |
|||
queryOperator: 'equals', |
|||
type: 'w-select', |
|||
clearable: true, |
|||
colspan: 2, |
|||
options: [], |
|||
}, |
|||
], |
|||
toolbarActions: [ |
|||
'query', |
|||
'reset', |
|||
'separator', |
|||
{ |
|||
extend: 'add', |
|||
click: () => { |
|||
templateParamsDialogRef.value.show(); |
|||
}, |
|||
afterClick: () => { |
|||
templateParamsAddEditFormRef.value.setStatus('add'); |
|||
state.templateParamsDialogTitle = '新增变量'; |
|||
templateParamsAddEditFormRef.value.getFields()['reportExcelTemplate'].options = state.templateArray; |
|||
state.paramsType = null; |
|||
if (state.tableSelectedTemplateId) { |
|||
templateParamsAddEditFormRef.value.setFieldValue('paramsIsCommon', false); |
|||
templateParamsAddEditFormRef.value.setFieldValue('reportExcelTemplate', state.tableSelectedTemplateId); |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
extend: 'edit', |
|||
click: () => { |
|||
templateParamsDialogRef.value.show(); |
|||
}, |
|||
afterClick: (args) => { |
|||
templateParamsAddEditFormRef.value.setStatus('edit'); |
|||
state.templateParamsDialogTitle = '编辑变量'; |
|||
templateParamsAddEditFormRef.value.getFields()['reportExcelTemplate'].options = state.templateArray; |
|||
templateParamsAddEditFormRef.value.setData(args.selected); |
|||
state.paramsType = args.selected.paramsType; |
|||
if (args.selected['paramsIsCommon']) { |
|||
templateParamsAddEditFormRef.value.setFieldValue('reportExcelTemplate', null); |
|||
} else if (args.selected['reportExcelTemplate']) { |
|||
templateParamsAddEditFormRef.value.setFieldValue('reportExcelTemplate', args.selected['reportExcelTemplate']); |
|||
} |
|||
if (state.paramsType === 'LIST') { |
|||
const urlSearchParams = new URLSearchParams(); |
|||
urlSearchParams.append('criteria', JSON.stringify({ fieldName: 'reportExcelTemplateParams', operator: 'equals', value: args.selected.id })); |
|||
axios |
|||
.get(Environment.apiContextPath('api/excel/template/params/list?pageable=false&sortBy=sortNo'), { params: urlSearchParams }) |
|||
.then((resp) => { |
|||
if (resp.data.content) { |
|||
templateParamsListFieldGridRef.value.setLocalData(resp.data.content); |
|||
} |
|||
}) |
|||
.catch((error) => { |
|||
console.info('error====', error); |
|||
}); |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
extend: 'clone', |
|||
click: () => { |
|||
templateParamsDialogRef.value.show(); |
|||
}, |
|||
afterClick: (args) => { |
|||
templateParamsAddEditFormRef.value.setStatus('clone'); |
|||
state.templateParamsDialogTitle = '复制变量'; |
|||
templateParamsAddEditFormRef.value.getFields()['reportExcelTemplate'].options = state.templateArray; |
|||
templateParamsAddEditFormRef.value.setData(args.selected); |
|||
state.paramsType = args.selected.paramsType; |
|||
if (args.selected['paramsIsCommon']) { |
|||
templateParamsAddEditFormRef.value.setFieldValue('reportExcelTemplate', null); |
|||
} else if (args.selected['reportExcelTemplate']) { |
|||
templateParamsAddEditFormRef.value.setFieldValue('reportExcelTemplate', args.selected['reportExcelTemplate']); |
|||
} |
|||
if (state.paramsType === 'LIST') { |
|||
const urlSearchParams = new URLSearchParams(); |
|||
urlSearchParams.append('criteria', JSON.stringify({ fieldName: 'reportExcelTemplateParams', operator: 'equals', value: args.selected.id })); |
|||
axios |
|||
.get(Environment.apiContextPath('api/excel/template/params/list?pageable=false&sortBy=sortNo'), { params: urlSearchParams }) |
|||
.then((resp) => { |
|||
if (resp.data.content) { |
|||
templateParamsListFieldGridRef.value.setLocalData(resp.data.content); |
|||
} |
|||
}) |
|||
.catch((error) => { |
|||
console.info('error====', error); |
|||
}); |
|||
} |
|||
}, |
|||
}, |
|||
'remove', |
|||
'separator', |
|||
], |
|||
columns: [ |
|||
{ name: 'paramsCode', label: '变量编码' }, |
|||
{ name: 'paramsName', label: '变量名称' }, |
|||
{ |
|||
name: 'paramsType', |
|||
label: '变量类型', |
|||
format: (val, row) => { |
|||
if (val === 'SING') { |
|||
return '单值'; |
|||
} else if (val === 'LIST') { |
|||
return '列表'; |
|||
} else { |
|||
return val; |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'paramsIsCommon', |
|||
label: '变量是否通用', |
|||
format: (val, row) => { |
|||
if (val === true) { |
|||
return '所有模板均可使用'; |
|||
} else if (val === false) { |
|||
return '指定模板可使用'; |
|||
} else { |
|||
return val; |
|||
} |
|||
}, |
|||
}, |
|||
{ name: 'reportExcelTemplateName', label: '所属模板名称' }, |
|||
{ name: 'lastModifier', label: '最后修改人' }, |
|||
{ name: 'lastModifyDate', label: '最后修改时间' }, |
|||
], |
|||
}; |
|||
|
|||
const templateParamsDialog = { |
|||
buttons: [ |
|||
{ |
|||
icon: 'beenhere', |
|||
label: '提交', |
|||
click: async () => { |
|||
const validate = await templateParamsAddEditFormRef.value.validate(); |
|||
if (validate) { |
|||
const formStatus = templateParamsAddEditFormRef.value.getStatus(); |
|||
const formData = templateParamsAddEditFormRef.value.getData(); |
|||
if (formData.reportExcelTemplate) { |
|||
formData.reportExcelTemplate = { id: formData.reportExcelTemplate }; |
|||
} else { |
|||
formData.reportExcelTemplate = null; |
|||
} |
|||
const fieldRows = templateParamsListFieldGridRef.value.getRows(); |
|||
let url = Environment.apiContextPath('api/excel/template/params'); |
|||
if (formStatus === 'edit') { |
|||
url += '/edit'; |
|||
formData.id = templateParamsGridRef.value.getSelectedRows()[0].id; |
|||
fieldRows.forEach((item) => { |
|||
delete item.reportExcelTemplateParams; |
|||
}); |
|||
} else { |
|||
url += '/add'; |
|||
} |
|||
const resp = await axios |
|||
.post(url, { |
|||
params: formData, |
|||
list: formData.paramsType === 'LIST' ? fieldRows : [], |
|||
}) |
|||
.catch((error) => { |
|||
console.info('error====', error); |
|||
NotifyManager.error('操作失败'); |
|||
}); |
|||
NotifyManager.info('保存成功'); |
|||
templateParamsDialogRef.value.hide(); |
|||
templateParamsGridRef.value.refresh(); |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
icon: 'published_with_changes', |
|||
label: 'SQL校验', |
|||
click: async () => { |
|||
const paramsSqlValue = templateParamsAddEditFormRef.value.getFieldValue('paramsSql'); |
|||
if (!paramsSqlValue) { |
|||
NotifyManager.warn('取值SQL为空'); |
|||
return; |
|||
} |
|||
const requestParams = { |
|||
method: 'POST', |
|||
headers: { 'content-type': 'text/plain;charset=utf-8;' }, |
|||
data: paramsSqlValue, |
|||
url: Environment.apiContextPath('api/jdbc/fetchMetaDataBySql'), |
|||
}; |
|||
const resp = await axios(requestParams); |
|||
if (resp && resp.data) { |
|||
NotifyManager.info('校验通过'); |
|||
} else { |
|||
NotifyManager.error('校验失败'); |
|||
} |
|||
}, |
|||
}, |
|||
], |
|||
formFields: [ |
|||
{ label: '变量编码', name: 'paramsCode', type: 'w-text', requiredIf: true }, |
|||
{ label: '变量名称', name: 'paramsName', type: 'w-text', requiredIf: true }, |
|||
{ |
|||
label: '变量类型', |
|||
name: 'paramsType', |
|||
type: 'w-select', |
|||
requiredIf: true, |
|||
options: [ |
|||
{ label: '单值', value: 'SING' }, |
|||
{ label: '列表', value: 'LIST' }, |
|||
], |
|||
'onUpdate:modelValue': (value) => { |
|||
state.paramsType = value; |
|||
}, |
|||
}, |
|||
{ |
|||
label: '变量是否通用', |
|||
name: 'paramsIsCommon', |
|||
type: 'w-select', |
|||
requiredIf: true, |
|||
options: [ |
|||
{ label: '所有模板均可使用', value: true }, |
|||
{ label: '指定模板可使用', value: false }, |
|||
], |
|||
'onUpdate:modelValue': (value) => { |
|||
if (typeof value === 'boolean' && value === false) { |
|||
templateParamsAddEditFormRef.value.setFieldValue('reportExcelTemplate', state.tableSelectedTemplateId); |
|||
} else { |
|||
templateParamsAddEditFormRef.value.setFieldValue('reportExcelTemplate', null); |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
label: '所属模板', |
|||
name: 'reportExcelTemplate', |
|||
type: 'w-select', |
|||
colspan: 2, |
|||
requiredIf: true, |
|||
options: [], |
|||
showIf: (args) => { |
|||
const value = args.form?.getFieldValue('paramsIsCommon'); |
|||
if (typeof value === 'boolean' && value === false) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
label: '取值SQL', |
|||
name: 'paramsSql', |
|||
type: 'w-textarea', |
|||
requiredIf: true, |
|||
colspan: 'full', |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
const templateParamsListFieldGrid = { |
|||
toolbarActions: [ |
|||
{ |
|||
name: 'generateFieldsBySql', |
|||
icon: 'playlist_add_circle', |
|||
label: '根据取值SQL生成', |
|||
click: async () => { |
|||
const paramsSqlValue = templateParamsAddEditFormRef.value.getFieldValue('paramsSql'); |
|||
if (!paramsSqlValue) { |
|||
NotifyManager.warn('取值SQL为空,无法生成'); |
|||
return; |
|||
} |
|||
const requestParams = { |
|||
method: 'POST', |
|||
headers: { 'content-type': 'text/plain;charset=utf-8;' }, |
|||
data: paramsSqlValue, |
|||
url: Environment.apiContextPath('api/jdbc/fetchMetaDataBySql'), |
|||
}; |
|||
const resp: any = await axios(requestParams).catch((error) => { |
|||
console.info('error====', error); |
|||
NotifyManager.error('操作失败'); |
|||
}); |
|||
const listFieldsRow = { name: '', desc: '' }; |
|||
const listFields = <any>[]; |
|||
if (resp.data) { |
|||
resp.data.forEach((item, index) => { |
|||
listFields.push({ ...listFieldsRow, ...{ sortNo: index + 1, name: item.columnName, type: item.columnTypeName } }); |
|||
}); |
|||
templateParamsListFieldGridRef.value.setLocalData(listFields); |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
extend: 'edit', |
|||
click: () => { |
|||
state.templateParamsListFieldDialogTitle = '编辑字段'; |
|||
templateParamsListFieldDialogRef.value.show(); |
|||
}, |
|||
afterClick: (args) => { |
|||
templateParamsListFieldAddEditFormRef.value.setStatus('edit'); |
|||
templateParamsListFieldAddEditFormRef.value.setData(args.selected); |
|||
}, |
|||
}, |
|||
{ |
|||
extend: 'remove', |
|||
click: (args) => { |
|||
if (args.tickeds) { |
|||
templateParamsListFieldGridRef.value.removeLocalData(args.tickeds); |
|||
} else { |
|||
templateParamsListFieldGridRef.value.removeLocalData(args.selecteds); |
|||
} |
|||
}, |
|||
}, |
|||
], |
|||
columns: [ |
|||
{ name: 'sortNo', label: '排序号' }, |
|||
{ name: 'name', label: '字段名' }, |
|||
{ name: 'desc', label: '字段描述' }, |
|||
], |
|||
}; |
|||
|
|||
const templateParamsListFieldDialog = { |
|||
buttons: [ |
|||
{ |
|||
icon: 'save', |
|||
label: '确定', |
|||
click: async () => { |
|||
const validateResult = await templateParamsListFieldAddEditFormRef.value.validate(); |
|||
if (validateResult) { |
|||
const formData = templateParamsListFieldAddEditFormRef.value.getData(); |
|||
const formStatus = templateParamsListFieldAddEditFormRef.value.getStatus(); |
|||
if (formStatus === 'add') { |
|||
templateParamsListFieldGridRef.value.addRow(formData, null); |
|||
} else { |
|||
templateParamsListFieldGridRef.value.replaceRow({ |
|||
...formData, |
|||
_rowKey_: templateParamsListFieldGridRef.value.getSelectedRows()[0]['_rowKey_'], |
|||
}); |
|||
} |
|||
templateParamsListFieldDialogRef.value.hide(); |
|||
} |
|||
}, |
|||
}, |
|||
], |
|||
formFields: [ |
|||
{ label: '字段名', name: 'name', type: 'w-text', requiredIf: true }, |
|||
{ label: '字段描述', name: 'desc', type: 'w-text' }, |
|||
], |
|||
}; |
|||
|
|||
const getTemplateListFun = async () => { |
|||
const resp = await axios.get(Environment.apiContextPath('api/excel/template?pageable=false')); |
|||
if (resp && resp.data?.content) { |
|||
const options = <any>[]; |
|||
resp.data.content.forEach((item) => { |
|||
options.push({ label: item.templateName, value: item.id }); |
|||
}); |
|||
state.templateArray = options; |
|||
templateParamsGridRef.value.getQueryForm().getFields()['reportExcelTemplate'].options = options; |
|||
|
|||
setTimeout(() => { |
|||
if (state.tableSelectedTemplateId) { |
|||
templateParamsGridRef.value.getQueryForm().setFieldValue('reportExcelTemplate', state.tableSelectedTemplateId); |
|||
} |
|||
// 设置完所属模板后加载变量列表的数据 |
|||
templateParamsGridRef.value.refresh(); |
|||
}, 100); |
|||
} |
|||
}; |
|||
const show = (tableSelected) => { |
|||
dialogRef.value.show(); |
|||
if (tableSelected && tableSelected.length > 0) { |
|||
state.tableSelectedTemplateId = tableSelected[0].id; |
|||
} else { |
|||
state.tableSelectedTemplateId = null; |
|||
} |
|||
nextTick(() => { |
|||
getTemplateListFun(); |
|||
}); |
|||
}; |
|||
const hide = () => { |
|||
dialogRef.value.hide(); |
|||
}; |
|||
|
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
</script> |
@ -0,0 +1,234 @@ |
|||
<template> |
|||
<w-dialog ref="dialogRef" title="选取变量" width="80%" height="80%" :buttons="dialog.buttons"> |
|||
<div class="h-full"> |
|||
<div class="h-[300px]"> |
|||
<w-grid |
|||
ref="templateParamsGridRef" |
|||
dense |
|||
sort-no |
|||
title="模板变量列表" |
|||
:auto-fetch-data="false" |
|||
:data-url="Environment.apiContextPath('api/excel/template/params')" |
|||
:columns="templateParamsGrid.columns" |
|||
:toolbar-actions="templateParamsGrid.toolbarActions" |
|||
:config-button="false" |
|||
:query-form-cols-num="3" |
|||
:query-form-fields="templateParamsGrid.queryFormFields" |
|||
:sort-by="['-lastModifyDate']" |
|||
@row-click="templateParamsGridClick" |
|||
></w-grid> |
|||
</div> |
|||
<div style="height: calc(100% - 300px)"> |
|||
<w-grid |
|||
v-show="state.showListFieldGrid" |
|||
ref="templateParamsListFieldGridRef" |
|||
dense |
|||
sort-no |
|||
:config-button="false" |
|||
:hide-bottom="false" |
|||
:pageable="false" |
|||
title="字段列表" |
|||
:auto-fetch-data="false" |
|||
:fetch-data-url="Environment.apiContextPath('api/excel/template/params/list')" |
|||
:columns="templateParamsListFieldGrid.columns" |
|||
></w-grid> |
|||
</div> |
|||
</div> |
|||
</w-dialog> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, reactive, nextTick } from 'vue'; |
|||
import { useQuasar } from 'quasar'; |
|||
import { axios, Environment, NotifyManager, OperatorTypeEnum } from 'platform-core'; |
|||
|
|||
const $q = useQuasar(); |
|||
const emit = defineEmits<{ |
|||
( |
|||
e: 'setParams', // 设置模板变量 |
|||
paramsCode: string | Array<string>, // 变量编码(列表类型返回数组) |
|||
): void; |
|||
}>(); |
|||
|
|||
const dialogRef = ref(); |
|||
const templateParamsGridRef = ref(); |
|||
const templateParamsListFieldGridRef = ref(); |
|||
|
|||
const state = reactive({ |
|||
templateArray: [], |
|||
tableSelectedTemplateId: null, |
|||
showListFieldGrid: false, |
|||
}); |
|||
|
|||
const dialog = { |
|||
buttons: [ |
|||
{ |
|||
icon: 'save', |
|||
label: '确定', |
|||
click: async () => { |
|||
const paramsSelected = templateParamsGridRef.value.getSelectedRows(); |
|||
const listFieldSelected = templateParamsListFieldGridRef.value.getSelectedRows(); |
|||
if (paramsSelected && paramsSelected.length > 0) { |
|||
if (paramsSelected[0].paramsType === 'LIST' && (!listFieldSelected || listFieldSelected.length === 0)) { |
|||
const listFieldRows = templateParamsListFieldGridRef.value.getRows(); |
|||
const arr = <string[]>[]; |
|||
listFieldRows.forEach((item) => { |
|||
arr.push('#{' + item.name + '}'); |
|||
}); |
|||
emit('setParams', arr); |
|||
hide(); |
|||
return; |
|||
} else if (paramsSelected[0].paramsType === 'LIST' && listFieldSelected && listFieldSelected.length > 0) { |
|||
emit('setParams', '#{' + listFieldSelected[0].name + '}'); |
|||
hide(); |
|||
return; |
|||
} |
|||
} else { |
|||
NotifyManager.warn('请选择要插入的变量'); |
|||
return; |
|||
} |
|||
}, |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
const templateParamsGrid = reactive({ |
|||
queryFormFields: [ |
|||
{ label: '变量名称', name: 'paramsName', type: 'w-text' }, |
|||
{ |
|||
label: '变量类型', |
|||
name: 'paramsType', |
|||
type: 'w-select', |
|||
queryOperator: 'equals', |
|||
options: [ |
|||
{ label: '单值', value: 'SING' }, |
|||
{ label: '列表', value: 'LIST' }, |
|||
], |
|||
}, |
|||
{ |
|||
label: '所属模板', |
|||
name: 'reportExcelTemplate', |
|||
type: 'w-select', |
|||
queryOperator: 'equals', |
|||
clearable: true, |
|||
colspan: 2, |
|||
options: [], |
|||
}, |
|||
], |
|||
toolbarActions: ['query', 'reset'], |
|||
columns: [ |
|||
{ name: 'paramsCode', label: '变量编码' }, |
|||
{ name: 'paramsName', label: '变量名称' }, |
|||
{ |
|||
name: 'paramsType', |
|||
label: '变量类型', |
|||
format: (val) => { |
|||
if (val === 'SING') { |
|||
return '单值'; |
|||
} else if (val === 'LIST') { |
|||
return '列表'; |
|||
} else { |
|||
return val; |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'paramsIsCommon', |
|||
label: '变量是否通用', |
|||
format: (val) => { |
|||
if (val === true) { |
|||
return '所有模板均可使用'; |
|||
} else if (val === false) { |
|||
return '指定模板可使用'; |
|||
} else { |
|||
return val; |
|||
} |
|||
}, |
|||
}, |
|||
{ name: 'reportExcelTemplateName', label: '所属模板名称' }, |
|||
{ name: 'lastModifier', label: '最后修改人' }, |
|||
{ name: 'lastModifyDate', label: '最后修改时间' }, |
|||
], |
|||
}); |
|||
|
|||
const templateParamsListFieldGrid = reactive({ |
|||
columns: [ |
|||
{ name: 'name', label: '字段名' }, |
|||
{ name: 'desc', label: '字段描述' }, |
|||
{ |
|||
name: 'formatType', |
|||
label: '格式化类型', |
|||
format: (val) => { |
|||
if (val && OperatorTypeEnum[val]) { |
|||
return OperatorTypeEnum[val]; |
|||
} else { |
|||
return val; |
|||
} |
|||
}, |
|||
}, |
|||
{ name: 'formatValue', label: '格式化值' }, |
|||
], |
|||
}); |
|||
|
|||
const templateParamsGridClick = (args) => { |
|||
const { evt, row, index } = args; |
|||
if (row.paramsType === 'LIST') { |
|||
state.showListFieldGrid = true; |
|||
const urlSearchParams = new URLSearchParams(); |
|||
urlSearchParams.append('criteria', JSON.stringify({ fieldName: 'reportExcelTemplateParams', operator: 'equals', value: row.id })); |
|||
axios |
|||
.get(Environment.apiContextPath('api/excel/template/params/list?pageable=false&sortBy=sortNo'), { params: urlSearchParams }) |
|||
.then((resp) => { |
|||
if (resp.data.content) { |
|||
templateParamsListFieldGridRef.value.setLocalData(resp.data.content); |
|||
} |
|||
}) |
|||
.catch((error) => { |
|||
console.info('error====', error); |
|||
}); |
|||
} else { |
|||
// 清空字段列表 |
|||
templateParamsListFieldGridRef.value.setLocalData([]); |
|||
// 隐藏字段列表 |
|||
state.showListFieldGrid = false; |
|||
} |
|||
}; |
|||
const getTemplateListFun = async () => { |
|||
const resp = await axios.get(Environment.apiContextPath('api/excel/template?pageable=false')); |
|||
if (resp && resp.data?.content) { |
|||
const options = <any>[]; |
|||
resp.data.content.forEach((item) => { |
|||
options.push({ label: item.templateName, value: item.id }); |
|||
}); |
|||
state.templateArray = options; |
|||
templateParamsGridRef.value.getQueryForm().getFields()['reportExcelTemplate'].options = options; |
|||
|
|||
setTimeout(() => { |
|||
if (state.tableSelectedTemplateId) { |
|||
templateParamsGridRef.value.getQueryForm().setFieldValue('reportExcelTemplate', state.tableSelectedTemplateId); |
|||
} |
|||
// 设置完所属模板后加载变量列表的数据 |
|||
templateParamsGridRef.value.refresh(); |
|||
}, 100); |
|||
} |
|||
}; |
|||
const show = (templateId) => { |
|||
dialogRef.value.show(); |
|||
if (templateId) { |
|||
state.tableSelectedTemplateId = templateId; |
|||
} else { |
|||
state.tableSelectedTemplateId = null; |
|||
} |
|||
nextTick(() => { |
|||
getTemplateListFun(); |
|||
}); |
|||
}; |
|||
const hide = () => { |
|||
dialogRef.value.hide(); |
|||
}; |
|||
|
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
</script> |
@ -0,0 +1,34 @@ |
|||
<template> |
|||
<w-dialog ref="dialogRef" :title="state.dialogTitle" width="100%" height="100%" :maximized="true" :can-maximize="false" body-padding="0px 0px 0px 0px"> |
|||
<ExcelReport v-if="state.dataId" ref="excelReportRef" :template-id="state.dataId" style="height: 100%"></ExcelReport> |
|||
</w-dialog> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, reactive } from 'vue'; |
|||
|
|||
import ExcelReport from './ExcelReport.vue'; |
|||
|
|||
const dialogRef = ref(); |
|||
const excelReportRef = ref(); |
|||
const state = reactive({ |
|||
dialogTitle: '模板制定', |
|||
dataId: '', |
|||
}); |
|||
|
|||
const show = (tableSelectedRow: any) => { |
|||
state.dataId = tableSelectedRow.id; |
|||
state.dialogTitle = '模板报表查看——' + tableSelectedRow.templateName + '(' + tableSelectedRow.templateCode + ')'; |
|||
dialogRef.value.show(); |
|||
}; |
|||
const hide = () => { |
|||
dialogRef.value.hide(); |
|||
}; |
|||
|
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped></style> |
@ -0,0 +1,91 @@ |
|||
<template> |
|||
<w-dialog ref="dialogRef" title="属性配置" width="80%" height="80%" :buttons="dialog.buttons"> |
|||
<w-form ref="formRef" :cols-num="1" :fields="form.fields" class="p-1.5"> </w-form> |
|||
</w-dialog> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, nextTick } from 'vue'; |
|||
import { NotifyManager, Tools } from 'platform-core'; |
|||
|
|||
const emit = defineEmits<{ |
|||
( |
|||
e: 'setForEachParams', // 设置循环属性 |
|||
formData: any, // 循环属性对象 |
|||
): void; |
|||
}>(); |
|||
|
|||
const dialogRef = ref(); |
|||
const formRef = ref(); |
|||
|
|||
const dialog = { |
|||
buttons: [ |
|||
{ |
|||
icon: 'save', |
|||
label: '确定', |
|||
click: async () => { |
|||
const validate = await formRef.value.validate(); |
|||
if (validate) { |
|||
const formData = formRef.value.getData(); |
|||
let flag = false; |
|||
Object.keys(formData).forEach((item) => { |
|||
if (!Tools.isEmpty(formData[item])) { |
|||
flag = true; |
|||
} |
|||
}); |
|||
if (flag) { |
|||
emit('setForEachParams', formData); |
|||
dialogRef.value.hide(); |
|||
} else { |
|||
NotifyManager.warn('未配置任何属性'); |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
], |
|||
}; |
|||
const form = { |
|||
fields: [ |
|||
{ |
|||
label: '单元格格式', |
|||
name: 'cellFormat', |
|||
type: 'w-select', |
|||
options: [ |
|||
{ label: '纯文本', value: '1' }, |
|||
{ label: '数字格式-整数', value: '2' }, |
|||
{ label: '数字格式-两位小数', value: '3' }, |
|||
{ label: '数字格式-四位小数', value: '4' }, |
|||
{ label: '百分比整数', value: '5' }, |
|||
{ label: '百分比两位小数', value: '6' }, |
|||
{ label: '万元两位小数(示例:12万3456.00)', value: '7' }, |
|||
{ label: '千位符整数(示例:1,235)', value: '8' }, |
|||
{ label: '千位符两位小数(示例:1,234.56)', value: '9' }, |
|||
], |
|||
}, |
|||
{ |
|||
label: '单元格码值转换', |
|||
name: 'cellValueTransformed', |
|||
type: 'w-text', |
|||
hint: `当值匹配上时显示转换后的值,等号左边为原始值,右边为转换值,位置需对应,英文逗号分隔。示例:1,2=年,月 转换为空示例:-999=NULL`, |
|||
}, |
|||
{ label: '单元格批注', name: 'cellComment', type: 'w-textarea' }, |
|||
], |
|||
}; |
|||
|
|||
const show = (data) => { |
|||
dialogRef.value.show(); |
|||
nextTick(() => { |
|||
if (data) { |
|||
formRef.value.setData(data); |
|||
} |
|||
}); |
|||
}; |
|||
const hide = () => { |
|||
dialogRef.value.hide(); |
|||
}; |
|||
|
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
</script> |
@ -0,0 +1,250 @@ |
|||
<template> |
|||
<w-dialog ref="dialogRef" title="属性配置" width="80%" height="80%" :buttons="dialog.buttons"> |
|||
<w-form ref="formRef" :cols-num="1" :fields="form.fields" class="p-1.5"></w-form> |
|||
<w-dialog ref="listParamsDialogRef" dialog-title="选择循环的列表名" width="70%" height="70%" :buttons="listParamsDialog.buttons"> |
|||
<w-grid |
|||
ref="templateParamsGridRef" |
|||
title="模板变量列表" |
|||
sort-no |
|||
dense |
|||
:checkbox-selection="false" |
|||
:auto-fetch-data="false" |
|||
:data-url="Environment.apiContextPath('api/excel/template/params')" |
|||
:columns="templateParamsGrid.columns" |
|||
:toolbar-actions="templateParamsGrid.toolbarActions" |
|||
:query-form-cols-num="2" |
|||
:query-form-row-num="2" |
|||
:query-form-fields="templateParamsGrid.queryFormFields" |
|||
:sort-by="['-lastModifyDate']" |
|||
></w-grid> |
|||
</w-dialog> |
|||
</w-dialog> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, reactive, nextTick } from 'vue'; |
|||
import { axios, Environment, NotifyManager, Tools } from 'platform-core'; |
|||
|
|||
const emit = defineEmits<{ |
|||
( |
|||
e: 'setForEachParams', // 设置循环属性 |
|||
formData: any, // 循环属性对象 |
|||
): void; |
|||
}>(); |
|||
|
|||
const dialogRef = ref(); |
|||
const formRef = ref(); |
|||
const listParamsDialogRef = ref(); |
|||
const templateParamsGridRef = ref(); |
|||
|
|||
const state = reactive({ |
|||
templateArray: [], |
|||
tableSelectedTemplateId: null, |
|||
}); |
|||
|
|||
const dialog = { |
|||
buttons: [ |
|||
{ |
|||
icon: 'save', |
|||
label: '确定', |
|||
click: async () => { |
|||
const validate = await formRef.value.validate(); |
|||
if (validate) { |
|||
const formData = formRef.value.getData(); |
|||
let flag = false; |
|||
Object.keys(formData).forEach((item) => { |
|||
if (!Tools.isEmpty(formData[item])) { |
|||
flag = true; |
|||
} |
|||
}); |
|||
if (flag) { |
|||
emit('setForEachParams', formData); |
|||
dialogRef.value.hide(); |
|||
} else { |
|||
NotifyManager.warn('未配置任何属性'); |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
], |
|||
}; |
|||
const templateParamsGrid = { |
|||
queryFormFields: [ |
|||
{ label: '变量编码', name: 'paramsCode', type: 'w-text' }, |
|||
{ label: '变量名称', name: 'paramsName', type: 'w-text' }, |
|||
{ |
|||
label: '所属模板', |
|||
name: 'reportExcelTemplate', |
|||
type: 'w-select', |
|||
clearable: true, |
|||
queryOperator: 'equals', |
|||
options: [], |
|||
}, |
|||
{ |
|||
label: '变量类型', |
|||
name: 'paramsType', |
|||
type: 'w-select', |
|||
queryOperator: 'equals', |
|||
options: [ |
|||
{ label: '单值', value: 'SING' }, |
|||
{ label: '列表', value: 'LIST' }, |
|||
], |
|||
}, |
|||
], |
|||
toolbarActions: ['query', 'reset', 'separator'], |
|||
columns: [ |
|||
{ name: 'paramsCode', label: '变量编码' }, |
|||
{ name: 'paramsName', label: '变量名称' }, |
|||
{ |
|||
name: 'paramsType', |
|||
label: '变量类型', |
|||
format: (val) => { |
|||
if (val === 'SING') { |
|||
return '单值'; |
|||
} else if (val === 'LIST') { |
|||
return '列表'; |
|||
} else { |
|||
return val; |
|||
} |
|||
}, |
|||
}, |
|||
{ |
|||
name: 'paramsIsCommon', |
|||
label: '变量是否通用', |
|||
format: (val) => { |
|||
if (val === true) { |
|||
return '所有模板均可使用'; |
|||
} else if (val === false) { |
|||
return '指定模板可使用'; |
|||
} else { |
|||
return val; |
|||
} |
|||
}, |
|||
}, |
|||
{ name: 'reportExcelTemplateName', label: '所属模板名称' }, |
|||
{ name: 'lastModifier', label: '最后修改人' }, |
|||
{ name: 'lastModifyDate', label: '最后修改时间' }, |
|||
], |
|||
}; |
|||
const form = { |
|||
fields: [ |
|||
{ |
|||
label: '循环的列表名', |
|||
name: 'listName', |
|||
type: 'w-grid-select', |
|||
requiredIf: true, |
|||
displayValue: 'paramsName', |
|||
grid: { |
|||
title: '模板变量列表', |
|||
primaryKey: 'paramsCode', |
|||
sortNo: true, |
|||
dense: true, |
|||
dataUrl: Environment.apiContextPath('api/excel/template/params'), |
|||
columns: templateParamsGrid.columns, |
|||
queryFormFields: templateParamsGrid.queryFormFields, |
|||
queryFormColsNum: 2, |
|||
queryFormRowNum: 2, |
|||
sortBy: ['-lastModifyDate'], |
|||
}, |
|||
}, |
|||
{ label: '循环列表中一行数据占几行', name: 'dataRowNum', type: 'w-number' }, |
|||
{ |
|||
label: '单元格值相等时字体加粗', |
|||
name: 'fontWeight', |
|||
type: 'w-text', |
|||
hint: `当值匹配上任意值字体加粗,英文逗号分隔。示例:合计,不适用`, |
|||
}, |
|||
{ |
|||
label: '单元格值相等时字体加粗-该行后续列都加粗', |
|||
name: 'fontWeightRow', |
|||
type: 'w-select', |
|||
options: [ |
|||
{ label: '是', value: 'true' }, |
|||
{ label: '否', value: 'false' }, |
|||
], |
|||
}, |
|||
{ |
|||
label: '单元格根据值设置背景色', |
|||
name: 'cellBgColor', |
|||
type: 'w-text', |
|||
hint: `当值匹配上时设置对应的背景色,等号左边为单元格值,右边为设置的背景色,位置需对应,英文逗号分隔。示例:不达标,达标,其他=#f44336,#2196f3,#bdbdbd`, |
|||
}, |
|||
{ |
|||
label: '单元格根据值设置背景色-该行后续列都设置相同背景色', |
|||
name: 'cellBgColorRow', |
|||
type: 'w-select', |
|||
options: [ |
|||
{ label: '是', value: 'true' }, |
|||
{ label: '否', value: 'false' }, |
|||
], |
|||
}, |
|||
{ |
|||
label: '单元格码值转换', |
|||
name: 'cellValueTransformed', |
|||
type: 'w-text', |
|||
hint: `当值匹配上时显示转换后的值,等号左边为原始值,右边为转换值,位置需对应,英文逗号分隔。示例:1,2=年,月 转换为空示例:-999=NULL`, |
|||
}, |
|||
{ label: '单元格批注', name: 'cellComment', type: 'w-textarea', rows: 2 }, |
|||
], |
|||
}; |
|||
|
|||
const listParamsDialog = { |
|||
buttons: [ |
|||
{ |
|||
icon: 'save', |
|||
label: '确定', |
|||
click: () => { |
|||
const tableSelected = templateParamsGridRef.value.getSelectedRows(); |
|||
if (!tableSelected || tableSelected.length === 0) { |
|||
NotifyManager.info('请选择模板变量'); |
|||
return; |
|||
} |
|||
formRef.value.setFieldValue('listName', tableSelected[0].paramsCode); |
|||
listParamsDialogRef.value.hide(); |
|||
}, |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
const getTemplateListFun = async () => { |
|||
const resp = await axios.get(Environment.apiContextPath('api/excel/template?pageable=false')); |
|||
if (resp.data?.content) { |
|||
const options = <any>[]; |
|||
resp.data.content.forEach((item) => { |
|||
options.push({ label: item.templateName, value: item.id }); |
|||
}); |
|||
state.templateArray = options; |
|||
templateParamsGridRef.value.getQueryForm().getFields()['reportExcelTemplate'].options = options; |
|||
|
|||
setTimeout(() => { |
|||
if (state.tableSelectedTemplateId) { |
|||
templateParamsGridRef.value.getQueryForm().setFieldValue('reportExcelTemplate', state.tableSelectedTemplateId); |
|||
templateParamsGridRef.value.getQueryForm().setFieldValue('paramsType', 'LIST'); |
|||
} |
|||
templateParamsGridRef.value.refresh(); |
|||
}, 100); |
|||
} |
|||
}; |
|||
|
|||
const show = (templateId, data) => { |
|||
dialogRef.value.show(); |
|||
if (templateId) { |
|||
state.tableSelectedTemplateId = templateId; |
|||
} else { |
|||
state.tableSelectedTemplateId = null; |
|||
} |
|||
nextTick(() => { |
|||
if (data) { |
|||
formRef.value.setData(data); |
|||
} |
|||
}); |
|||
}; |
|||
const hide = () => { |
|||
dialogRef.value.hide(); |
|||
}; |
|||
|
|||
defineExpose({ |
|||
show, |
|||
hide, |
|||
}); |
|||
</script> |
@ -0,0 +1,339 @@ |
|||
<template> |
|||
<q-card ref="cardRef" flat bordered :style="cardHeightComponent"> |
|||
<q-card-section class="p-2.5"> |
|||
<div class="flex gap-2"> |
|||
<div class="grow"> |
|||
<q-file v-model="impFile" label="导入Excel文件" outlined dense accept=".xlsx,.xls" @update:model-value="loadExcel($event)"> |
|||
<template #append> |
|||
<q-icon name="attachment" /> |
|||
</template> |
|||
</q-file> |
|||
</div> |
|||
<div class="flex flex-none"> |
|||
<q-btn label="选取变量" :icon="PlatformIconEnum.选取变量" @click="selectParams"></q-btn> |
|||
</div> |
|||
<div class="flex flex-none"> |
|||
<q-btn-dropdown :icon="PlatformIconEnum.设置" label="循环属性配置" unelevated outline> |
|||
<q-list> |
|||
<q-item v-close-popup clickable> |
|||
<q-item-section> |
|||
<q-item-label @click="openConfigDialog('main')"><q-icon :name="PlatformIconEnum.更多查询"></q-icon> 整体配置(首列)</q-item-label> |
|||
</q-item-section> |
|||
</q-item> |
|||
<q-item v-close-popup clickable> |
|||
<q-item-section> |
|||
<q-item-label @click="openConfigDialog('column')"><q-icon :name="PlatformIconEnum.更多查询"></q-icon> 列配置(非首列)</q-item-label> |
|||
</q-item-section> |
|||
</q-item> |
|||
</q-list> |
|||
</q-btn-dropdown> |
|||
</div> |
|||
<div class="flex flex-none"> |
|||
<q-btn label="保存并关闭" :icon="PlatformIconEnum.保存" @click="save"></q-btn> |
|||
</div> |
|||
<div class="flex flex-none"> |
|||
<q-btn label="导出文件" :icon="PlatformIconEnum.下载" @click="downloadExcel"></q-btn> |
|||
</div> |
|||
</div> |
|||
</q-card-section> |
|||
<TemplateParamsSelectDialog ref="paramsSelectDialogRef" @set-params="setParams"></TemplateParamsSelectDialog> |
|||
<TemplateSetForEachParamsDialog ref="setForEachParamsDialogRef" @set-for-each-params="setForEachParams"></TemplateSetForEachParamsDialog> |
|||
<TemplateSetColumnParamsDialog ref="setColumnParamsDialogRef" @set-for-each-params="setForEachParams"></TemplateSetColumnParamsDialog> |
|||
<div id="luckysheet"></div> |
|||
</q-card> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, onMounted, computed, reactive } from 'vue'; |
|||
import { useRoute, onBeforeRouteUpdate } from 'vue-router'; |
|||
import LuckyExcel from 'luckyexcel'; |
|||
import { exportExcel } from './exportExcel'; |
|||
import { useQuasar } from 'quasar'; |
|||
import { axios, TagViewManager, Environment, PlatformIconEnum, Tools, NotifyManager } from 'platform-core'; |
|||
|
|||
import TemplateParamsSelectDialog from '@/views/excel_template/TemplateParamsSelectDialog.vue'; |
|||
import TemplateSetForEachParamsDialog from '@/views/excel_template/TemplateSetForeachPropsDialog.vue'; |
|||
import TemplateSetColumnParamsDialog from '@/views/excel_template/TemplateSetColumnParamsDialog.vue'; |
|||
|
|||
const gc = Environment.getConfigure(); |
|||
const $q = useQuasar(); |
|||
const impFile = ref(null); |
|||
const paramsSelectDialogRef = ref(); |
|||
const setForEachParamsDialogRef = ref(); |
|||
const setColumnParamsDialogRef = ref(); |
|||
const route = useRoute(); |
|||
const cardRef = ref(); |
|||
console.info('route------------', route); |
|||
|
|||
// 路由变更时 |
|||
onBeforeRouteUpdate((to, from, next) => { |
|||
console.info('to------------', to); |
|||
initExcel(to.query.templateId); |
|||
next(); |
|||
}); |
|||
|
|||
const state = reactive({ |
|||
cardY: 0, |
|||
}); |
|||
|
|||
const cardHeightComponent = computed(() => { |
|||
// 浏览器可视区域高度 |
|||
const screenHeight = $q.screen.height; |
|||
// 系统底部高度(一般用来显示XX版权所有那一层) |
|||
const footerHeight = gc.theme.footer.enable ? gc.theme.footer.height : 0; |
|||
const cardHeight = screenHeight - footerHeight - state.cardY - 10; |
|||
return { |
|||
height: cardHeight + 'px', |
|||
}; |
|||
}); |
|||
|
|||
const loadExcel = (value) => { |
|||
const { name } = value; |
|||
const suffixArr = name.split('.'); |
|||
const suffix = suffixArr[suffixArr.length - 1]; |
|||
if (suffix !== 'xlsx') { |
|||
NotifyManager.info('只能导入xlsx格式的Excel文件'); |
|||
return; |
|||
} |
|||
LuckyExcel.transformExcelToLucky(value, (exportJson) => { |
|||
if (exportJson.sheets === null || exportJson.sheets.length === 0) { |
|||
NotifyManager.info(`只能导入'xlsx'格式的Excel文件`); |
|||
return; |
|||
} |
|||
|
|||
window.luckysheet.destroy(); |
|||
|
|||
const data = [ |
|||
{ |
|||
...exportJson.sheets[0], |
|||
...{ |
|||
column: 26, |
|||
row: 30, |
|||
}, |
|||
}, |
|||
]; |
|||
luckysheetOptions.data = data; |
|||
window.luckysheet.create({ |
|||
lang: 'zh', |
|||
container: 'luckysheet', |
|||
showinfobar: false, |
|||
showtoolbar: true, // 是否显示工具栏,设置字体,边框,插入这些工具按钮。 |
|||
sheetFormulaBar: true, // 是否显示公式栏 |
|||
showsheetbar: false, // 是否显示底部sheet页按钮 |
|||
data: data, |
|||
title: exportJson.info.name, |
|||
userInfo: exportJson.info.name.creator, |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const downloadExcel = () => { |
|||
// eslint-disable-next-line |
|||
exportExcel(luckysheet.getAllSheets(), '下载'); |
|||
}; |
|||
|
|||
const luckysheetData = [ |
|||
{ |
|||
name: 'Cell', // 工作表名称 |
|||
color: '', // 工作表颜色 |
|||
index: 0, // 工作表索引 |
|||
status: 1, // 激活状态 |
|||
order: 0, // 工作表的下标 |
|||
hide: 0, // 是否隐藏 |
|||
defaultRowHeight: 19, // 自定义行高 |
|||
defaultColWidth: 73, // 自定义列宽 |
|||
column: 26, |
|||
row: 30, |
|||
celldata: [], // 初始化使用的单元格数据 |
|||
config: { |
|||
merge: {}, // 合并单元格 |
|||
rowlen: {}, // 表格行高 |
|||
columnlen: {}, // 表格列宽 |
|||
rowhidden: {}, // 隐藏行 |
|||
colhidden: {}, // 隐藏列 |
|||
borderInfo: [], // 边框 |
|||
authority: {}, // 工作表保护 |
|||
}, |
|||
scrollLeft: 0, // 左右滚动条位置 |
|||
scrollTop: 0, // 上下滚动条位置 |
|||
luckysheet_select_save: [], // 选中的区域 |
|||
calcChain: [], // 公式链 |
|||
isPivotTable: false, // 是否数据透视表 |
|||
pivotTable: {}, // 数据透视表设置 |
|||
filter_select: {}, // 筛选范围 |
|||
filter: null, // 筛选配置 |
|||
luckysheet_alternateformat_save: [], // 交替颜色 |
|||
luckysheet_alternateformat_save_modelCustom: [], // 自定义交替颜色 |
|||
luckysheet_conditionformat_save: {}, // 条件格式 |
|||
frozen: {}, // 冻结行列配置 |
|||
chart: [], // 图表配置 |
|||
zoomRatio: 1, // 缩放比例 |
|||
image: [], // 图片 |
|||
showGridLines: 1, // 是否显示网格线 |
|||
dataVerification: {}, // 数据验证配置 |
|||
}, |
|||
]; |
|||
const luckysheetOptions = { |
|||
container: 'luckysheet', // 设定DOM容器的id |
|||
lang: 'zh', // 设定表格语言 |
|||
title: '测试', // 设定表格名称 |
|||
allowEdit: true, // 是否允许编辑 |
|||
rowHeaderWidth: 46, // 行标题区域的宽度,如果设置为0,则表示隐藏行标题,也就是表格第一行,A,B,C这行,默认值46 |
|||
columnHeaderHeight: 20, // 列标题区域的高度,如果设置为0,则表示隐藏列标题,也就是序号这一行,默认值20 |
|||
showtoolbar: true, // 是否显示工具栏,设置字体,边框,插入这些工具按钮。 |
|||
showinfobar: false, // 是否显示顶部信息栏,luckysheet的logo,表格名称这一栏。 |
|||
userInfo: 'likunming', // 右上角用户信息,若需要展示,必须配合showinfobar使用 |
|||
sheetFormulaBar: true, // 是否显示公式栏 |
|||
defaultFontSize: 11, // 初始化默认字体大小,默认11 |
|||
showsheetbar: false, // 是否显示底部sheet页按钮 |
|||
data: luckysheetData, // 表格数据 |
|||
}; |
|||
const luckySheetEdit = () => { |
|||
luckysheetOptions.allowEdit = true; |
|||
luckysheetOptions.showtoolbar = true; |
|||
luckysheetOptions.sheetFormulaBar = true; |
|||
// eslint-disable-next-line |
|||
luckysheet.create(luckysheetOptions); |
|||
}; |
|||
const luckySheetSave = () => { |
|||
luckysheetOptions.allowEdit = false; |
|||
luckysheetOptions.showtoolbar = false; |
|||
luckysheetOptions.sheetFormulaBar = false; |
|||
// eslint-disable-next-line |
|||
luckysheet.create(luckysheetOptions); |
|||
}; |
|||
const selectParams = () => { |
|||
// eslint-disable-next-line |
|||
const selectRange = luckysheet.getRange(); |
|||
if (!selectRange || selectRange.length === 0) { |
|||
platformNotify($q, '请选择要插入变量的Excel单元格'); |
|||
return; |
|||
} else if (selectRange && selectRange.length > 1) { |
|||
platformNotify($q, '只支持单个选区选取变量'); |
|||
return; |
|||
} |
|||
paramsSelectDialogRef.value.dialogShow(route.query?.templateId); |
|||
}; |
|||
const setParams = (paramsCode: string | Array<string>) => { |
|||
if (typeof paramsCode === 'string') { |
|||
// eslint-disable-next-line |
|||
let rangeValue = luckysheet.getRangeValue(); |
|||
rangeValue[0][0] = { ...rangeValue[0][0], ...{ m: paramsCode, v: paramsCode } }; |
|||
// eslint-disable-next-line |
|||
luckysheet.setRangeValue(rangeValue); |
|||
} else { |
|||
// eslint-disable-next-line |
|||
const currRange = luckysheet.getRange(); |
|||
paramsCode.forEach((item, index) => { |
|||
const tmpRangeValue = [[{ m: item, v: item }]]; |
|||
const tmp = { |
|||
row: [currRange[0].row[0], currRange[0].row[1]], |
|||
column: [currRange[0].column[0] + index, currRange[0].column[1] + index], |
|||
}; |
|||
// eslint-disable-next-line |
|||
luckysheet.setRangeValue(tmpRangeValue, { range: tmp }); |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
// eslint-disable-next-line |
|||
const psReg = /\@\{(.*)\}\@/; |
|||
const openConfigDialog = (configType) => { |
|||
// eslint-disable-next-line |
|||
const range = luckysheet.getRange(); |
|||
// eslint-disable-next-line |
|||
const rangeValue = luckysheet.getRangeValue(); |
|||
if (!range) { |
|||
NotifyManager.info('请选择要配置属性的单元格'); |
|||
return; |
|||
} else if (range && range.length > 1) { |
|||
NotifyManager.info('多个选区无法配置属性,选择单元格既可'); |
|||
return; |
|||
} |
|||
let data = {}; |
|||
if (rangeValue[0] && rangeValue[0][0] && rangeValue[0][0].ps && rangeValue[0][0].ps.value) { |
|||
let tmp = rangeValue[0][0].ps.value.replaceAll('\n', ''); |
|||
data = JSON.parse(psReg.exec(tmp)[1]); |
|||
} |
|||
if (configType === 'main') { |
|||
setForEachParamsDialogRef.value.dialogShow(route.query?.templateId, data); |
|||
} else { |
|||
setColumnParamsDialogRef.value.dialogShow(data); |
|||
} |
|||
}; |
|||
|
|||
const setForEachParams = (formData) => { |
|||
// eslint-disable-next-line |
|||
const range = luckysheet.getRange(); |
|||
let tmp = '@{{\n'; |
|||
Object.keys(formData).forEach((item) => { |
|||
if (!Tools.isEmpty(formData[item])) { |
|||
tmp += '"' + item + '"' + ':' + '"' + formData[item] + '"' + ',' + '\n'; |
|||
} |
|||
}); |
|||
tmp = tmp.substring(0, tmp.length - 2); |
|||
tmp += '\n}}@'; |
|||
// eslint-disable-next-line |
|||
luckysheet.insertComment(range[0].row[0], range[0].column[0], tmp); |
|||
// 插入批注后调整批注框大小 |
|||
luckysheetOptions.data[0].data[range[0].row[0]][range[0].column[0]].ps.width = 300; |
|||
luckysheetOptions.data[0].data[range[0].row[0]][range[0].column[0]].ps.height = 150; |
|||
luckysheetOptions.data[0].data[range[0].row[0]][range[0].column[0]].ps.isShow = false; |
|||
// eslint-disable-next-line |
|||
luckysheet.refresh(); |
|||
}; |
|||
const save = async () => { |
|||
// eslint-disable-next-line |
|||
if (!luckysheet.getAllSheets() || luckysheet.getAllSheets().length === 0) { |
|||
NotifyManager.info('系统异常,未获取到需要保存的Sheet数据'); |
|||
return; |
|||
} |
|||
// 覆盖celldata数据缩减传给后端的数据量,该数据仅用以初始化表格,无需保存,初始化时可使用data属性。 |
|||
// eslint-disable-next-line |
|||
const saveData = { ...luckysheet.getAllSheets()[0], ...{ celldata: [] } }; |
|||
// 保存 |
|||
let requestParams = { |
|||
method: 'POST', |
|||
headers: { 'content-type': 'application/json;charset=utf-8;' }, |
|||
data: { templateConfig: JSON.stringify(saveData) }, |
|||
url: Environment.apiContextPath('api/excel/template/updateConfig/') + route.query.templateId, |
|||
}; |
|||
const resp = await axios(requestParams).catch((error) => { |
|||
console.info('error====', error); |
|||
NotifyManager.error('保存失败'); |
|||
}); |
|||
NotifyManager.error('保存成功'); |
|||
// 关闭当前页 |
|||
// const index = TagViewManager.getTagViews().findIndex((item) => item.fullPath.indexOf(route.query.templateId) > -1); |
|||
TagViewManager.removeTagView({ mode: 'current' }); |
|||
}; |
|||
|
|||
const initExcel = async (templateId?: any) => { |
|||
if (window.luckysheet) { |
|||
window.luckysheet.destroy(); |
|||
} |
|||
const resp = await axios.get(Environment.apiContextPath('api/excel/template/') + (templateId ?? route.query.templateId)); |
|||
if (resp?.data?.templateConfig) { |
|||
luckysheetOptions.data = [eval('(' + resp.data.templateConfig + ')')]; |
|||
} |
|||
// eslint-disable-next-line |
|||
luckysheet.create(luckysheetOptions); |
|||
}; |
|||
|
|||
onMounted(async () => { |
|||
state.cardY = cardRef.value.$el.getBoundingClientRect()?.y; |
|||
initExcel(); |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped> |
|||
#luckysheet { |
|||
margin: 0px; |
|||
padding: 0px; |
|||
position: absolute; |
|||
width: 100%; |
|||
left: 0px; |
|||
top: 70px; |
|||
bottom: 0px; |
|||
} |
|||
</style> |
@ -0,0 +1,319 @@ |
|||
import Excel from 'exceljs'; |
|||
import FileSaver from 'file-saver'; |
|||
|
|||
const exportExcel = function (luckysheet, value) { |
|||
// 参数为luckysheet.getluckysheetfile()获取的对象
|
|||
// 1.创建工作簿,可以为工作簿添加属性
|
|||
const workbook = new Excel.Workbook(); |
|||
// 2.创建表格,第二个参数可以配置创建什么样的工作表
|
|||
if (Object.prototype.toString.call(luckysheet) === '[object Object]') { |
|||
luckysheet = [luckysheet]; |
|||
} |
|||
luckysheet.forEach(function (table) { |
|||
if (table.data.length === 0) return true; |
|||
// ws.getCell('B2').fill = fills.
|
|||
const worksheet = workbook.addWorksheet(table.name); |
|||
const merge = (table.config && table.config.merge) || {}; |
|||
const borderInfo = (table.config && table.config.borderInfo) || {}; |
|||
// 3.设置单元格合并,设置单元格边框,设置单元格样式,设置值
|
|||
setStyleAndValue(table.data, worksheet); |
|||
setMerge(merge, worksheet); |
|||
setBorder(borderInfo, worksheet); |
|||
return true; |
|||
}); |
|||
|
|||
// return
|
|||
// 4.写入 buffer
|
|||
const buffer = workbook.xlsx.writeBuffer().then((data) => { |
|||
// console.log('data', data)
|
|||
const blob = new Blob([data], { |
|||
type: 'application/vnd.ms-excel;charset=utf-8', |
|||
}); |
|||
console.log('导出成功!'); |
|||
FileSaver.saveAs(blob, `${value}.xlsx`); |
|||
}); |
|||
return buffer; |
|||
}; |
|||
|
|||
const setMerge = function (luckyMerge = {}, worksheet) { |
|||
const mergearr = Object.values(luckyMerge); |
|||
mergearr.forEach(function (elem) { |
|||
// elem格式:{r: 0, c: 0, rs: 1, cs: 2}
|
|||
// 按开始行,开始列,结束行,结束列合并(相当于 K10:M12)
|
|||
worksheet.mergeCells(elem.r + 1, elem.c + 1, elem.r + elem.rs, elem.c + elem.cs); |
|||
}); |
|||
}; |
|||
|
|||
const setBorder = function (luckyBorderInfo, worksheet) { |
|||
if (!Array.isArray(luckyBorderInfo)) return; |
|||
// console.log('luckyBorderInfo', luckyBorderInfo)
|
|||
luckyBorderInfo.forEach(function (elem) { |
|||
// 现在只兼容到borderType 为range的情况
|
|||
// console.log('ele', elem)
|
|||
if (elem.rangeType === 'range') { |
|||
const border = borderConvert(elem.borderType, elem.style, elem.color); |
|||
const rang = elem.range[0]; |
|||
// console.log('range', rang)
|
|||
const row = rang.row; |
|||
const column = rang.column; |
|||
for (let i = row[0] + 1; i < row[1] + 2; i++) { |
|||
for (let y = column[0] + 1; y < column[1] + 2; y++) { |
|||
worksheet.getCell(i, y).border = border; |
|||
} |
|||
} |
|||
} |
|||
if (elem.rangeType === 'cell') { |
|||
// col_index: 2
|
|||
// row_index: 1
|
|||
// b: {
|
|||
// color: '#d0d4e3'
|
|||
// style: 1
|
|||
// }
|
|||
const { col_index, row_index } = elem.value; |
|||
const borderData = Object.assign({}, elem.value); |
|||
delete borderData.col_index; |
|||
delete borderData.row_index; |
|||
const border = addborderToCell(borderData, row_index, col_index); |
|||
// console.log('bordre', border, borderData)
|
|||
worksheet.getCell(row_index + 1, col_index + 1).border = border; |
|||
} |
|||
// console.log(rang.column_focus + 1, rang.row_focus + 1)
|
|||
// worksheet.getCell(rang.row_focus + 1, rang.column_focus + 1).border = border
|
|||
}); |
|||
}; |
|||
const setStyleAndValue = function (cellArr, worksheet) { |
|||
if (!Array.isArray(cellArr)) return; |
|||
cellArr.forEach(function (row, rowid) { |
|||
row.every(function (cell, columnid) { |
|||
if (!cell) return true; |
|||
const fill = fillConvert(cell.bg); |
|||
|
|||
const font = fontConvert(cell.ff, cell.fc, cell.bl, cell.it, cell.fs, cell.cl, cell.ul); |
|||
const alignment = alignmentConvert(cell.vt, cell.ht, cell.tb, cell.tr); |
|||
let value = ''; |
|||
|
|||
if (cell.f) { |
|||
value = { formula: cell.f, result: cell.v }; |
|||
} else if (!cell.v && cell.ct && cell.ct.s) { |
|||
// xls转为xlsx之后,内部存在不同的格式,都会进到富文本里,即值不存在与cell.v,而是存在于cell.ct.s之后
|
|||
// value = cell.ct.s[0].v
|
|||
cell.ct.s.forEach((arr) => { |
|||
value += arr.v; |
|||
}); |
|||
} else { |
|||
value = cell.v; |
|||
} |
|||
// style 填入到_value中可以实现填充色
|
|||
const letter = createCellPos(columnid); |
|||
const target = worksheet.getCell(letter + (rowid + 1)); |
|||
// console.log('1233', letter + (rowid + 1))
|
|||
for (const key in fill) { |
|||
target.fill = fill; |
|||
break; |
|||
} |
|||
target.font = font; |
|||
target.alignment = alignment; |
|||
target.value = value; |
|||
|
|||
return true; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const fillConvert = function (bg) { |
|||
if (!bg) { |
|||
return {}; |
|||
} |
|||
// const bgc = bg.replace('#', '')
|
|||
const fill = { |
|||
type: 'pattern', |
|||
pattern: 'solid', |
|||
fgColor: { argb: bg.replace('#', '') }, |
|||
}; |
|||
return fill; |
|||
}; |
|||
|
|||
const fontConvert = function (ff = 0, fc = '#000000', bl = 0, it = 0, fs = 10, cl = 0, ul = 0) { |
|||
// luckysheet:ff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线)
|
|||
const luckyToExcel = { |
|||
0: '微软雅黑', |
|||
1: '宋体(Song)', |
|||
2: '黑体(ST Heiti)', |
|||
3: '楷体(ST Kaiti)', |
|||
4: '仿宋(ST FangSong)', |
|||
5: '新宋体(ST Song)', |
|||
6: '华文新魏', |
|||
7: '华文行楷', |
|||
8: '华文隶书', |
|||
9: 'Arial', |
|||
10: 'Times New Roman ', |
|||
11: 'Tahoma ', |
|||
12: 'Verdana', |
|||
num2bl: function (num) { |
|||
return num === 0 ? false : true; |
|||
}, |
|||
}; |
|||
// 出现Bug,导入的时候ff为luckyToExcel的val
|
|||
|
|||
const font = { |
|||
name: typeof ff === 'number' ? luckyToExcel[ff] : ff, |
|||
family: 1, |
|||
size: fs, |
|||
color: { argb: fc.replace('#', '') }, |
|||
bold: luckyToExcel.num2bl(bl), |
|||
italic: luckyToExcel.num2bl(it), |
|||
underline: luckyToExcel.num2bl(ul), |
|||
strike: luckyToExcel.num2bl(cl), |
|||
}; |
|||
|
|||
return font; |
|||
}; |
|||
|
|||
const alignmentConvert = function (vt = 'default', ht = 'default', tb = 'default', tr = 'default') { |
|||
// luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转)
|
|||
const luckyToExcel = { |
|||
vertical: { |
|||
0: 'middle', |
|||
1: 'top', |
|||
2: 'bottom', |
|||
default: 'top', |
|||
}, |
|||
horizontal: { |
|||
0: 'center', |
|||
1: 'left', |
|||
2: 'right', |
|||
default: 'left', |
|||
}, |
|||
wrapText: { |
|||
0: false, |
|||
1: false, |
|||
2: true, |
|||
default: false, |
|||
}, |
|||
textRotation: { |
|||
0: 0, |
|||
1: 45, |
|||
2: -45, |
|||
3: 'vertical', |
|||
4: 90, |
|||
5: -90, |
|||
default: 0, |
|||
}, |
|||
}; |
|||
|
|||
const alignment = { |
|||
vertical: luckyToExcel.vertical[vt], |
|||
horizontal: luckyToExcel.horizontal[ht], |
|||
wrapText: luckyToExcel.wrapText[tb], |
|||
textRotation: luckyToExcel.textRotation[tr], |
|||
}; |
|||
return alignment; |
|||
}; |
|||
|
|||
const borderConvert = function (borderType, style = 1, color = '#000') { |
|||
// 对应luckysheet的config中borderinfo的的参数
|
|||
if (!borderType) { |
|||
return {}; |
|||
} |
|||
const luckyToExcel = { |
|||
type: { |
|||
'border-all': 'all', |
|||
'border-top': 'top', |
|||
'border-right': 'right', |
|||
'border-bottom': 'bottom', |
|||
'border-left': 'left', |
|||
}, |
|||
style: { |
|||
0: 'none', |
|||
1: 'thin', |
|||
2: 'hair', |
|||
3: 'dotted', |
|||
4: 'dashDot', // 'Dashed',
|
|||
5: 'dashDot', |
|||
6: 'dashDotDot', |
|||
7: 'double', |
|||
8: 'medium', |
|||
9: 'mediumDashed', |
|||
10: 'mediumDashDot', |
|||
11: 'mediumDashDotDot', |
|||
12: 'slantDashDot', |
|||
13: 'thick', |
|||
}, |
|||
}; |
|||
const template = { |
|||
style: luckyToExcel.style[style], |
|||
color: { argb: color.replace('#', '') }, |
|||
}; |
|||
const border = {}; |
|||
if (luckyToExcel.type[borderType] === 'all') { |
|||
border['top'] = template; |
|||
border['right'] = template; |
|||
border['bottom'] = template; |
|||
border['left'] = template; |
|||
} else { |
|||
border[luckyToExcel.type[borderType]] = template; |
|||
} |
|||
// console.log('border', border)
|
|||
return border; |
|||
}; |
|||
|
|||
function addborderToCell(borders, row_index, col_index) { |
|||
const border = {}; |
|||
const luckyExcel = { |
|||
type: { |
|||
l: 'left', |
|||
r: 'right', |
|||
b: 'bottom', |
|||
t: 'top', |
|||
}, |
|||
style: { |
|||
0: 'none', |
|||
1: 'thin', |
|||
2: 'hair', |
|||
3: 'dotted', |
|||
4: 'dashDot', // 'Dashed',
|
|||
5: 'dashDot', |
|||
6: 'dashDotDot', |
|||
7: 'double', |
|||
8: 'medium', |
|||
9: 'mediumDashed', |
|||
10: 'mediumDashDot', |
|||
11: 'mediumDashDotDot', |
|||
12: 'slantDashDot', |
|||
13: 'thick', |
|||
}, |
|||
}; |
|||
// console.log('borders', borders)
|
|||
for (const bor in borders) { |
|||
// console.log(bor)
|
|||
if (borders[bor].color.indexOf('rgb') === -1) { |
|||
border[luckyExcel.type[bor]] = { |
|||
style: luckyExcel.style[borders[bor].style], |
|||
color: { argb: borders[bor].color.replace('#', '') }, |
|||
}; |
|||
} else { |
|||
border[luckyExcel.type[bor]] = { |
|||
style: luckyExcel.style[borders[bor].style], |
|||
color: { argb: borders[bor].color }, |
|||
}; |
|||
} |
|||
} |
|||
|
|||
return border; |
|||
} |
|||
|
|||
function createCellPos(n) { |
|||
const ordA = 'A'.charCodeAt(0); |
|||
|
|||
const ordZ = 'Z'.charCodeAt(0); |
|||
const len = ordZ - ordA + 1; |
|||
let s = ''; |
|||
while (n >= 0) { |
|||
s = String.fromCharCode((n % len) + ordA) + s; |
|||
|
|||
n = Math.floor(n / len) - 1; |
|||
} |
|||
return s; |
|||
} |
|||
|
|||
export { exportExcel }; |
@ -0,0 +1,27 @@ |
|||
const cellFormatMap = new Map(); |
|||
// 纯文本
|
|||
cellFormatMap.set('1', { fa: '@', t: 's' }); |
|||
// 数字格式-整数
|
|||
cellFormatMap.set('2', { fa: '0', t: 'n' }); |
|||
// 数字格式-两位小数
|
|||
cellFormatMap.set('3', { fa: '0.00', t: 'n' }); |
|||
// 数字格式-四位小数
|
|||
cellFormatMap.set('4', { fa: '0.0000', t: 'n' }); |
|||
// 百分比整数
|
|||
cellFormatMap.set('5', { fa: '0%', t: 'n' }); |
|||
// 百分比两位小数
|
|||
cellFormatMap.set('6', { fa: '0.00%', t: 'n' }); |
|||
// 万元两位小数(示例:12万3456.00)
|
|||
cellFormatMap.set('7', { fa: 'w0.00', t: 'n' }); |
|||
// 千位符整数(示例:1,235)
|
|||
cellFormatMap.set('8', { fa: '#,##0', t: 'n' }); |
|||
// 千位符两位小数(示例:1,234.56)
|
|||
cellFormatMap.set('9', { fa: '#,##0.00', t: 'n' }); |
|||
|
|||
/** |
|||
* 根据下拉框所选值获取单元格格式化对象 |
|||
* @param index |
|||
*/ |
|||
export function getExcelCellFormatObject(index: string) { |
|||
return cellFormatMap.get(index); |
|||
} |
Loading…
Reference in new issue