﻿<template>
	<div>
		<el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="90%">
			<template #header>
				<div style="color: #fff">
					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>
					<span> {{ props.title }} </span>
				</div>
			</template>
			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="130" :rules="rules">
				<el-row :gutter="10">
					<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="mb20">
						<el-form-item :label="$t('名称')" prop="name">
							<el-input v-model="state.ruleForm.name" />
						</el-form-item>
					</el-col>
					<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="mb20">
						<el-form-item :label="$t('分组')" prop="groupId">
							<el-select v-model="state.ruleForm.groupId" filterable clearable>
								<el-option v-for="item in state.groupList" :key="item.id" :label="item.name" :value="item.id" />
							</el-select>
						</el-form-item>
					</el-col>
					<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="mb20">
						<el-form-item :label="$t('描述')" prop="description">
							<el-input v-model="state.ruleForm.description" />
						</el-form-item>
					</el-col>
					<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="mb20">
						<el-form-item :label="$t('数据源类型')" prop="dsType">
							<g-sys-dict v-model="state.ruleForm.dsType" code="ReportConfigDsTypeEnum" render-as="select" />
						</el-form-item>
					</el-col>
					<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="mb20" v-if="state.ruleForm.dsType === ReportConfigDsTypeEnum.NUMBER_0">
						<el-form-item :label="$t('数据源')" prop="dataSource">
							<el-select v-model="state.ruleForm.dataSource" class="w100">
								<el-option v-for="item in state.dataSourceList" :key="item.id" :label="item.name" :value="item.id" />
							</el-select>
						</el-form-item>
					</el-col>
					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20" :style="[state.ruleForm.dsType === ReportConfigDsTypeEnum.NUMBER_0 ? '' : 'display:none;']">
						<el-form-item :label="$t('脚本语句')" prop="sqlScript">
							<div ref="sqlScriptMonacoEditorRef" style="width: 100%; height: 300px; border: 1px solid var(--el-border-color)"></div>
							<el-button ref="parseSqlButtonRef" type="success" plain style="margin-top: 8px" @click="parseSqlClick">{{ $t('解析Sql') }}</el-button>
						</el-form-item>
					</el-col>
					<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="mb20" v-if="state.ruleForm.dsType === ReportConfigDsTypeEnum.NUMBER_1">
						<el-form-item :label="$t('接口地址')" prop="apiUrl">
							<el-input v-model="state.ruleForm.apiUrl" />
						</el-form-item>
					</el-col>
					<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="mb20" v-if="state.ruleForm.dsType === ReportConfigDsTypeEnum.NUMBER_1">
						<el-form-item :label="$t('接口请求方式')" prop="apiHttpMethod">
							<el-select v-model="state.ruleForm.apiHttpMethod" class="w100">
								<el-option key="Get" label="Get" value="Get" />
								<el-option key="Post" label="Post" value="Post" />
							</el-select>
						</el-form-item>
					</el-col>
					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20" :style="[state.ruleForm.dsType === ReportConfigDsTypeEnum.NUMBER_1 ? '' : 'display:none;']">
						<el-form-item :label="$t('接口参数')" prop="apiParams">
							<div ref="apiParamsMonacoEditorRef" style="width: 100%; height: 300px; border: 1px solid var(--el-border-color)"></div>
						</el-form-item>
					</el-col>
				</el-row>
			</el-form>
			<el-tabs v-model="state.activeName" class="report-config-tab">
				<el-tab-pane :label="$t('字段')" name="field">
					<vxe-grid ref="vFieldGrid" v-bind="fieldConfig.gridOptions" :data="state.fieldListData">
						<template #toolbar_buttons>
							<el-space>
								<el-button type="primary" size="small" icon="ele-Plus" @click="() => add(vFieldGrid!, beforeAddField)">{{ $t('新增') }}</el-button>
								<el-button size="small" @click="() => insert(vFieldGrid!)">{{ $t('插入') }}</el-button>
								<el-button type="danger" size="small" icon="ele-Minus" plain @click="() => remove(vFieldGrid!)">{{ $t('删除') }}</el-button>
							</el-space>
						</template>
						<template #toolbar_tools> </template>
						<template #row_isSummary="{ row }">
							{{ row.isSummary ? '√' : '' }}
						</template>
						<template #row_visible="{ row }">
							{{ row.visible ? '√' : '' }}
						</template>
					</vxe-grid>
				</el-tab-pane>
				<el-tab-pane :label="$t('参数')" name="param">
					<div style="display: flex; height: 100%">
						<vxe-grid ref="vParamGrid" style="flex: 1" v-bind="paramConfig.gridOptions" :data="state.paramListData">
							<template #toolbar_buttons>
								<el-space>
									<el-button type="primary" size="small" icon="ele-Plus" @click="() => add(vParamGrid!)">{{ $t('新增') }}</el-button>
									<el-button size="small" @click="() => insert(vParamGrid!)">{{ $t('插入') }}</el-button>
									<el-button type="danger" size="small" icon="ele-Minus" plain @click="() => remove(vParamGrid!)">{{ $t('删除') }}</el-button>
								</el-space>
							</template>
							<template #toolbar_tools> </template>
						</vxe-grid>
						<el-card style="margin-left: 4px" :header="$t('内置参数')" headerClass="sys-report-config-param-build-in-card-header">
							<el-row style="width: 240px" v-for="(item, index) in buildInParams" :key="index">
								<el-col :span="11">{{ item.name }}</el-col>
								<el-col :span="13">{{ item.description }}</el-col>
							</el-row>
						</el-card>
					</div>
				</el-tab-pane>
			</el-tabs>
			<template #footer>
				<span class="dialog-footer">
					<el-button icon="ele-CircleCloseFilled" @click="cancel">{{ $t('取 消') }}</el-button>
					<el-button type="primary" icon="ele-CircleCheckFilled" @click="submit">{{ $t('确 定') }}</el-button>
				</span>
			</template>
		</el-dialog>
	</div>
</template>

<script setup lang="ts" name="sysReportConfigEditForm">
import { computed, onMounted, reactive, ref } from 'vue';
import { ElInput, ElMessage } from 'element-plus';
import { getAPI } from '/@/utils/axios-utils';
import { ReportConfigDsTypeEnum, ReportDataSourceOutput, SysReportField, SysReportGroup, SysReportParam, UpdateReportConfigInput } from '/@/api-services';
import { SysReportConfigApi, SysReportDataSourceApi, SysReportGroupApi } from '/@/api-services/api';
import { VxeGrid } from 'vxe-table';
import { useI18n } from 'vue-i18n';

import * as monaco from 'monaco-editor';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import { format } from 'sql-formatter';
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
import { sqlSugarDbTypes } from '../inputCtrlType';

const { t } = useI18n();

const props = defineProps({
	title: String,
});
const emits = defineEmits(['handleQuery']);
const ruleFormRef = ref();
const vFieldGrid = ref<InstanceType<typeof VxeGrid>>();
const vParamGrid = ref<InstanceType<typeof VxeGrid>>();
const sqlScriptMonacoEditorRef = ref();
const apiParamsMonacoEditorRef = ref();
const parseSqlButtonRef = ref<InstanceType<typeof ElInput>>();

// TODO: monaco 封装成组件
// 设置 monaco worker
self.MonacoEnvironment = {
	getWorker: function (workerId: string, label: string) {
		if (label === 'json') {
			return new jsonWorker();
		}
		return new editorWorker();
	},
};

// 注册 monaco sql 格式化
monaco.languages.registerDocumentFormattingEditProvider('sql', {
	provideDocumentFormattingEdits: (model: monaco.editor.ITextModel): monaco.languages.ProviderResult<monaco.languages.TextEdit[]> => {
		return [
			{
				text: formatSql(model.getValue()),
				range: model.getFullModelRange(),
			},
		];
	},
});

/** 格式化 Sql */
const formatSql = (value: string): string => {
	return format(value, {
		tabWidth: 4,
		useTabs: false,
		keywordCase: 'upper',
		dataTypeCase: 'upper',
		functionCase: 'upper',
		logicalOperatorNewline: 'before',
		language: 'transactsql',
	});
};

// 初始化monacoEditor对象
var sqlScriptMonacoEditor: monaco.editor.IStandaloneCodeEditor;
const initSqlScriptMonacoEditor = () => {
	sqlScriptMonacoEditor = monaco.editor.create(sqlScriptMonacoEditorRef.value, {
		theme: 'vs', // 主题 vs vs-dark hc-black
		value: '', // 默认显示的值
		language: 'sql',
		formatOnPaste: true,
		wordWrap: 'on', //自动换行，注意大小写
		wrappingIndent: 'indent',
		folding: true, // 是否折叠
		foldingHighlight: true, // 折叠等高线
		foldingStrategy: 'indentation', // 折叠方式  auto | indentation
		showFoldingControls: 'always', // 是否一直显示折叠 always | mouSEOver
		disableLayerHinting: true, // 等宽优化
		emptySelectionClipboard: false, // 空选择剪切板
		selectionClipboard: false, // 选择剪切板
		automaticLayout: true, // 自动布局
		codeLens: false, // 代码镜头
		scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
		colorDecorators: true, // 颜色装饰器
		accessibilitySupport: 'auto', // 辅助功能支持  "auto" | "off" | "on"
		lineNumbers: 'on', // 行号 取值： "on" | "off" | "relative" | "interval" | function
		lineNumbersMinChars: 5, // 行号最小字符   number
		//enableSplitViewResizing: false,
		readOnly: false, //是否只读  取值 true | false
		renderLineHighlight: 'none', // 当前行突出显示方式  "all" | "line" | "none" | "gutter"
		minimap: {
			// 缩略图
			size: 'fill',
			enabled: false,
		},
	});

	// 添加按钮
	sqlScriptMonacoEditor.addOverlayWidget({
		getId: function () {
			return 'monaco.editor.neuz.parseSqlButton';
		},
		getDomNode: function () {
			return parseSqlButtonRef.value?.$el;
		},
		getPosition: function () {
			return {
				preference: monaco.editor.OverlayWidgetPositionPreference.TOP_RIGHT_CORNER,
			};
		},
	});
};

// 初始化monacoEditor对象
var apiParamsMonacoEditor: monaco.editor.IStandaloneCodeEditor;
const initApiParamsMonacoEditor = () => {
	apiParamsMonacoEditor = monaco.editor.create(apiParamsMonacoEditorRef.value, {
		theme: 'vs', // 主题 vs vs-dark hc-black
		value: '', // 默认显示的值
		language: 'json',
		formatOnPaste: true,
		wordWrap: 'on', //自动换行，注意大小写
		wrappingIndent: 'indent',
		folding: true, // 是否折叠
		foldingHighlight: true, // 折叠等高线
		foldingStrategy: 'indentation', // 折叠方式  auto | indentation
		showFoldingControls: 'always', // 是否一直显示折叠 always | mouSEOver
		disableLayerHinting: true, // 等宽优化
		emptySelectionClipboard: false, // 空选择剪切板
		selectionClipboard: false, // 选择剪切板
		automaticLayout: true, // 自动布局
		codeLens: false, // 代码镜头
		scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
		colorDecorators: true, // 颜色装饰器
		accessibilitySupport: 'auto', // 辅助功能支持  "auto" | "off" | "on"
		lineNumbers: 'on', // 行号 取值： "on" | "off" | "relative" | "interval" | function
		lineNumbersMinChars: 5, // 行号最小字符   number
		//enableSplitViewResizing: false,
		readOnly: false, //是否只读  取值 true | false
		renderLineHighlight: 'none', // 当前行突出显示方式  "all" | "line" | "none" | "gutter"
	});
};

/** 后端内置参数 */
const buildInParams = [
	{ name: '@curTenantId', description: '当前租户Id' },
	{ name: '@curUserId', description: '当前用户Id' },
];

const fieldConfig = reactive({
	gridOptions: useVxeTable<SysReportField>(
		{
			id: 'sysReportConfigEditForm_fields',
			columns: [
				{ type: 'checkbox', width: 36, fixed: 'left' },
				{ field: 'fieldName', title: t('字段名'), width: 200, editRender: { name: 'ElInput', props: { size: 'small' } }, dragSort: true },
				{ field: 'title', title: t('字段标题'), width: 200, editRender: { name: 'ElInput', props: { size: 'small' } } },
				{ field: 'isSummary', title: t('是否合计'), width: 90, editRender: { name: 'ElSwitch', props: { size: 'small' } }, slots: { default: 'row_isSummary' } },
				{ field: 'visible', title: t('是否显示'), width: 90, editRender: { name: 'ElSwitch', props: { size: 'small' } }, slots: { default: 'row_visible' } },
				{ field: 'groupTitle', title: t('分组标题'), width: 200, editRender: { name: 'ElInput', props: { size: 'small' } } },
				{ field: 'width', title: t('列宽'), width: 80, editRender: { name: 'ElInputNumber', props: { size: 'small', min: 0, controlsPosition: 'right' } } },
			],
		},
		{
			align: 'left',
			columnConfig: {
				width: 130, // 列默认宽度
			},
			toolbarConfig: {
				refresh: false,
				import: false,
				export: false,
				print: false,
				zoom: false,
				custom: false,
			},
			rowConfig: {
				drag: true,
			},
			editConfig: {
				trigger: 'click',
				mode: 'row',
				// showStatus: true,
				autoClear: false,
			},
			keyboardConfig: {
				isArrow: true,
			},
			editRules: {
				fieldName: [{ required: true, message: t('字段名不能为空') }],
			},
		}
	),
});

const paramConfig = reactive({
	gridOptions: useVxeTable<SysReportField>(
		{
			id: 'sysReportConfigEditForm_param',
			columns: [
				{ type: 'checkbox', width: 36, fixed: 'left' },
				{ field: 'paramName', title: t('参数名'), editRender: { name: 'ElInput', props: { size: 'small' } } },
				{ field: 'title', title: t('参数标题'), editRender: { name: 'ElInput', props: { size: 'small' } } },
				{
					field: 'inputCtrl',
					title: t('输入控件类型'),
					editRender: {
						name: 'ElSelect',
						options: sqlSugarDbTypes,
						props: { size: 'small', filterable: true },
					},
				},
				{ field: 'defaultValue', title: t('默认值'), editRender: { name: 'ElInput', props: { size: 'small' } } },
			],
		},
		{
			align: 'left',
			columnConfig: {
				width: 130, // 列默认宽度
			},
			toolbarConfig: {
				refresh: false,
				import: false,
				export: false,
				print: false,
				zoom: false,
				custom: false,
			},
			editConfig: {
				trigger: 'click',
				mode: 'row',
				// showStatus: true,
				autoClear: false,
			},
			keyboardConfig: {
				isArrow: true,
			},
			editRules: {
				paramName: [{ required: true, message: t('参数名不能为空') }],
				title: [{ required: true, message: t('参数标题不能为空') }],
				inputCtrl: [{ required: true, message: t('输入控件类型不能为空') }],
			},
		}
	),
});

const state = reactive({
	isShowDialog: false,
	ruleForm: {} as UpdateReportConfigInput,
	activeName: 'field',
	/** 字段列表数据 */
	fieldListData: [] as SysReportField[],
	/** 参数列表数据 */
	paramListData: [] as SysReportParam[],
	/** 数据源列表数据 */
	dataSourceList: [] as ReportDataSourceOutput[],
	/** 分组列表数据 */
	groupList: [] as SysReportGroup[],
});

const rules = computed(() => {
	const data = {
		name: [{ required: true, message: t('名称不能为空') }],
		dsType: [{ required: true, message: t('数据源类型不能为空') }],
	} as any;

	if (state.ruleForm.dsType == ReportConfigDsTypeEnum.NUMBER_0) {
		data.dataSource = [{ required: true, message: t('数据源不能为空') }];
	} else {
		data.apiUrl = [{ required: true, message: t('接口地址不能为空') }];
		data.apiHttpMethod = [{ required: true, message: t('接口请求方式不能为空') }];
	}

	return data;
});

onMounted(() => {
	getAPI(SysReportDataSourceApi)
		.apiSysReportDataSourceGetDataSourceListGet()
		.then((result) => {
			if (result.data.type !== 'success') {
				return;
			}

			state.dataSourceList = result.data.result!;
		});
	getAPI(SysReportGroupApi)
		.apiSysReportGroupGetListGet()
		.then((res) => {
			state.groupList = res.data.result!;
		});
});

// 打开弹窗
const openDialog = (row: any) => {
	state.ruleForm = JSON.parse(JSON.stringify(row));

	state.fieldListData = state.ruleForm.fields ? JSON.parse(state.ruleForm.fields) : [];
	state.paramListData = state.ruleForm.params ? JSON.parse(state.ruleForm.params) : [];

	state.isShowDialog = true;
	ruleFormRef.value?.resetFields();

	const defaultSqlValue = `-- SELECT * FROM xxx WHERE number = @number AND createTime >= @time AND tenantId=@curTenantId
-- sp_name`;

	// 延迟拿值防止取不到
	setTimeout(() => {
		if (sqlScriptMonacoEditor == null || sqlScriptMonacoEditor == undefined) initSqlScriptMonacoEditor();
		sqlScriptMonacoEditor!.setValue(state.ruleForm.sqlScript == undefined || state.ruleForm.sqlScript == null || state.ruleForm.sqlScript == '' ? defaultSqlValue : state.ruleForm.sqlScript);

		if (apiParamsMonacoEditor == null || apiParamsMonacoEditor == undefined) initApiParamsMonacoEditor();
		apiParamsMonacoEditor!.setValue(state.ruleForm.apiParams ?? '');
	}, 100);
};

// 关闭弹窗
const closeDialog = () => {
	emits('handleQuery');
	state.isShowDialog = false;
};

// 取消
const cancel = () => {
	state.isShowDialog = false;
};

// 提交
const submit = () => {
	ruleFormRef.value.validate(async (valid: boolean) => {
		if (!valid) return;

		// 构造提交数据
		const fieldFullData = vFieldGrid.value!.getFullData();
		const paramFullData = vParamGrid.value!.getFullData();
		const record = state.ruleForm;
		record.sqlScript = sqlScriptMonacoEditor.getValue();
		record.apiParams = apiParamsMonacoEditor.getValue();
		record.fields = JSON.stringify(fieldFullData);
		record.params = JSON.stringify(paramFullData);

		if (state.ruleForm.id != undefined && state.ruleForm.id > 0) {
			await getAPI(SysReportConfigApi).apiSysReportConfigUpdatePost(record);
		} else {
			await getAPI(SysReportConfigApi).apiSysReportConfigAddPost(record);
		}
		closeDialog();
	});
};

/** 在新增字段前执行 */
const beforeAddField = (row: SysReportField): Promise<void> => {
	row.isSummary = false;
	row.visible = true;
	row.width = 0;

	return Promise.resolve();
};

/** 解析SQL */
const parseSqlClick = () => {
	const paramFullData = vParamGrid.value!.getFullData();
	const execParams: Record<string, string> = {};
	for (const item of paramFullData) {
		if (item.paramName) {
			execParams[item.paramName] = '';
		}
	}

	return getAPI(SysReportConfigApi)
		.apiSysReportConfigParseSqlPost({ dataSource: state.ruleForm.dataSource, sqlScript: sqlScriptMonacoEditor.getValue(), execParams: execParams })
		.then((res) => {
			ElMessage.success('解析成功');

			const fieldList = vFieldGrid.value!.getFullData();
			const fieldNames = res.data.result?.fieldNames ?? [];

			fieldNames.forEach((fieldName: string) => {
				if (fieldList.some((field) => field.fieldName === fieldName)) return;

				const field: SysReportField = { fieldName: fieldName, isSummary: false, visible: true, width: 0 };
				vFieldGrid.value!.insertAt(field, -1);
			});
		});
};

/** 新增行 */
const add = async (grid: InstanceType<typeof VxeGrid>, beforeAdd: ((row: any) => Promise<void>) | undefined = undefined): Promise<void> => {
	try {
		const newRow = {};
		beforeAdd && (await beforeAdd(newRow));

		const { row } = await grid.insertAt(newRow, -1);
		await grid.setEditRow(row, true);
	} catch (err: any) {
		console.log(err);
		ElMessage.error(err.message || err);
	}
};

/** 插入行 */
const insert = async (grid: InstanceType<typeof VxeGrid>): Promise<void> => {
	try {
		const newRow = {};

		const curRow = grid.getCurrentRecord();
		const { row } = await grid.insertAt(newRow, curRow);
		await grid.setEditRow(row, true);
	} catch (err: any) {
		console.log(err);
		ElMessage.error(err.message || err);
	}
};

/** 删除行 */
const remove = async (grid: InstanceType<typeof VxeGrid>): Promise<void> => {
	try {
		let checkedRows = grid.getCheckboxRecords();
		if (checkedRows === null || checkedRows.length === 0) {
			if (checkedRows === null) {
				checkedRows = [];
			}
			const curRow = grid.getCurrentRecord();
			if (curRow !== null) checkedRows.push(curRow);
		}

		if (!checkedRows || checkedRows.length === 0) return;

		await Promise.all(
			checkedRows.map(async (row: any): Promise<void> => {
				await grid.remove(row);
			})
		);

		await grid.clearCheckboxRow();
		await grid.clearCurrentRow();
	} catch (err: any) {
		console.log(err);
		ElMessage.error(err.message || err);
	}
};

// 导出对象
defineExpose({ openDialog });
</script>
<style lang="scss" scoped>
:deep(.el-dialog__body) {
	height: calc(100vh - 18px) !important;
	display: flex;
	flex-direction: column;
}

:deep(.el-tabs) {
	height: 100%;
	display: flex;

	.el-tabs__content {
		flex: 1;

		.el-tab-pane {
			height: 100%;
			display: flex;
			flex-direction: column;
		}
	}
}

.report-config-tab {
	flex: 1;
}
</style>
<style lang="scss">
.sys-report-config-param-build-in-card-header {
	padding: 8px;
}
</style>
