<template>
    <div class="manage-list-container">
        <div v-if="tableData">
            <!-- {{ query }} -->
            <!-- 筛选 -->
            <div class="handle-box">
                <template v-for="field in searchFields">
                    <el-input v-if="field.searchType == 'input' || field.searchType == 'input-number'"
                        v-model="query[field.field]" @keyup.enter.native="handleSearch" :placeholder="field.placeholder"
                        :prefix-icon="Filter" class="handle-input mr10"></el-input>
                    <el-select v-else-if="field.searchType == 'select'" v-model="query[field.field]" :clearable="true"
                        @change="handleSearch" :placeholder="field.placeholder" class="handle-select mr10">
                        <template #prefix>
                            <el-icon>
                                <Filter />
                            </el-icon>
                        </template>
                        <el-option v-for="optKey in Object.keys(field.options)" :key="optKey" :label="field.options[optKey]"
                            :value="optKey"></el-option>
                    </el-select>
                    <el-date-picker v-else-if="field.searchType == 'time-interval'" v-model="query[field.field]"
                        type="datetimerange" :shortcuts="dateTimePickerRangeShotcut" range-separator="至"
                        :start-placeholder="field.placeholder + ' 起始时间'" :end-placeholder="field.placeholder + '结束时间'"
                        :prefix-icon="Filter" style="width: 350px; margin-right: 10px;" />
                    <template v-else>{{ field }}</template>
                </template>
                <el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
                <el-button type="primary" :icon="Plus" @click="handleNew" v-permiss="props.editPermiss"
                    v-if="props.addFunc">新增记录</el-button>
                <el-button type="primary" :icon="Download" @click="exportFormVisible = true" v-permiss="props.editPermiss"
                    v-if="props.exportFunc">导出到文件</el-button>
            </div>
            <!-- 表格 -->
            <el-table :data="tableData" border class="table" ref="multipleTable" header-cell-class-name="table-header">
                <el-table-column prop="id" label="ID" align="center"></el-table-column>
                <el-table-column v-for="(field, index) in tableFields" :prop="field.prop" :label="field.label" :key="index"
                    align="center">
                    <template #default="scope" v-if="field.type == 'image'">
                        <el-image style="width: 100%; height: 100%;" :src="scope.row[field.prop]" fit="cover" />
                    </template>
                    <template #default="scope" v-else-if="field.type == 'time'">
                        {{
                        new Date(scope.row[field.prop] + 8 * 3600 * 1000).toISOString().replace('T', ' ').substring(0,19)
                        }}
                    </template>
                    <template #default="scope" v-else-if="field.type == 'longtext'">
                        <el-tooltip placement="top">
                            <template #content>
                                <p v-for="line in scope.row[field.prop].split(/[\r\n]/g)" style="max-width: 300px;">
                                    {{ line }}
                                </p>
                            </template>
                            <div class="oneLine">
                                {{ scope.row[field.prop] }}
                            </div>
                        </el-tooltip>
                    </template>
                </el-table-column>
                <el-table-column label="操作" width="220" align="center"
                    v-if="props.editFunc || props.deleteFunc || props.customEditHandle">
                    <template #default="scope">
                        <template v-if="props.customEditHandle">
                            <el-button text :icon="List"
                                @click="props.customEditHandle((scope as any).$index, (scope as any).row, getData)"
                                v-permiss="props.editPermiss">
                                管理
                            </el-button>
                        </template>
                        <template v-else>
                            <el-button text :icon="Edit" @click="handleEdit((scope as any).$index, (scope as any).row)"
                                v-permiss="props.editPermiss" v-if="props.editFunc">
                                编辑
                            </el-button>
                            <el-button text :icon="Delete" class="red"
                                @click="handleDelete((scope as any).$index, (scope as any).row)"
                                v-permiss="props.editPermiss" v-if="props.deleteFunc">
                                删除
                            </el-button>
                        </template>
                    </template>
                </el-table-column>
            </el-table>
            <!-- 分页 -->
            <div class="pagination">
                <el-pagination background layout="total, prev, pager, next" :current-page="query.pageIndex"
                    :page-size="query.pageSize" :total="pageTotal" @current-change="handlePageChange"></el-pagination>
            </div>
        </div>
        <div v-else style="padding-top: 11vh;">
            <el-empty description="暂无数据" />
        </div>

        <!-- 新增 / 编辑弹出框 -->
        <el-dialog :title="formId > 0 ? '编辑' : '新增'" v-model="editVisible" style="width: 40%; min-width: 280px;">
            <!-- {{ form }} -->
            <el-form ref="editForm" label-width="80px" :rules="rules" :model="form">
                <el-form-item v-if="editVisible" v-for="field in dialogFields" :label="field.label" :prop="field.field">
                    <el-input v-if="(formId > 0 ? field.editType : field.addType) == 'input'"
                        :placeholder="formId > 0 ? field.editPlaceholder : field.addPlaceholder" class="popup-item"
                        v-model="form[field.field]"></el-input>
                    <el-input v-else-if="(formId > 0 ? field.editType : field.addType) == 'input-number'" type="number"
                        :placeholder="formId > 0 ? field.editPlaceholder : field.addPlaceholder" class="popup-item"
                        v-model="form[field.field]"></el-input>
                    <el-input v-else-if="(formId > 0 ? field.editType : field.addType) == 'textarea'"
                        :placeholder="formId > 0 ? field.editPlaceholder : field.addPlaceholder" class="popup-item"
                        v-model="form[field.field]" type="textarea" :rows="4"></el-input>
                    <el-select v-else-if="(formId > 0 ? field.editType : field.addType) == 'select'" class="popup-item"
                        v-model="form[field.field]" :clearable="true">
                        <el-option v-for="optKey in Object.keys(field.options)" :key="optKey" :label="field.options[optKey]"
                            :value="optKey"></el-option>
                    </el-select>
                    <ImageUpload v-else-if="(formId > 0 ? field.editType : field.addType) == 'image'"
                        :imageUrl="form[field.field]" @change="(value: any) => form[field.field] = value" />
                    <el-input v-else-if="(formId > 0 ? field.editType : field.addType) == 'plainText'" class="popup-item"
                        v-model="form[field.field]" :disabled="true"></el-input>
                    <!-- {{ field }} -->
                </el-form-item>
            </el-form>
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="doMockData(formId > 0)" type="danger" round>随机填充测试数据</el-button>
                    <el-button @click="editVisible = false">取 消</el-button>
                    <el-button type="primary" @click="saveEdit(editForm)">确 定</el-button>
                </span>
            </template>
        </el-dialog>

        <!-- 导出 Excel 弹窗 -->
        <el-dialog v-model="exportFormVisible" title="导出选项">
            <!-- {{ exportConfig }} -->
            <el-form :model="form" label-width="80px">
                <el-form-item label="数据范围">
                    <el-radio-group v-model="exportConfig.withFilter">
                        <el-radio :label="false">全部数据</el-radio>
                        <el-radio :label="true">满足筛选条件的数据</el-radio>
                    </el-radio-group>
                </el-form-item>
                <el-form-item label="文件格式">
                    <el-radio-group v-model="exportConfig.ext">
                        <el-radio v-for="ext in supportExportFormatList" :label="ext">{{ ext }}</el-radio>
                    </el-radio-group>
                </el-form-item>
                <el-alert v-if="exportInfo" :title="exportInfo.info" :type="exportInfo.type" :closable="false" show-icon />
            </el-form>
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="exportFormVisible = false">取消</el-button>
                    <el-button type="primary" @click="handleExport">导出</el-button>
                </span>
            </template>
        </el-dialog>
    </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue';
import { FormInstance, FormRules, ElMessage, ElMessageBox } from 'element-plus';
import { Delete, Edit, Search, Plus, Filter, Download, List } from '@element-plus/icons-vue';
import * as xlsx from 'xlsx';
import Mock from 'mockjs';
import ImageUpload from './image-upload.vue';

const props = defineProps({
    // 获取列表 接口函数
    'listFunc': {
        type: Function,
        required: true,
    },
    // 新增 接口函数
    'addFunc': {
        type: Function,
        required: false,
    },
    // 修改 接口函数
    'editFunc': {
        type: Function,
        required: false,
    },
    // 删除 接口函数
    'deleteFunc': {
        type: Function,
        required: false,
    },
    // 导出 接口函数
    'exportFunc': {
        type: Function,
        required: true,
    },
    // 修改、删除、导出权限
    'editPermiss': {
        type: String,
        required: true,
    },

    // 自定义修改按钮点击事件
    'customEditHandle': {
        type: Function,
        required: false,
    }
})

// 筛选条件
const searchFields: any = ref([]);  // 筛选条件字段
const query = reactive({
    pageIndex: 1,
    pageSize: 10
    // 其他筛选条件
}); // 筛选条件的值

// 字段翻译字典（如 roleId -> roleName）
let fieldsMapper: any;

// 表格
const tableData: any = ref(null);   // 表格数据
const tableFields: any = ref([]);   // 表格列
const pageTotal = ref(0);           // 总页数

// 新增/编辑 弹窗
const dialogFields: any = ref([]);  // 弹窗字段
const editVisible = ref(false);     // 是否显示弹窗
let form = reactive({} as any);     // 弹窗字段的值
let idx: number = -1;               // 表格中编辑行的 index; 新增为 -1
let formId: number = -1;            // 数据库 id; 新增为 -1
let idFieldName: string = '';       // id列列名

// 添加/修改记录时表单验证
const editForm = ref<FormInstance>();
// const rules: FormRules = {};
const rules = ref({} as FormRules);

// mock数据
let mockData: any = []

// 导出 Excel 弹窗
const exportFormVisible: any = ref(false);
let exportFields: any = []          // 导出 excel 列表对应中文
const supportExportFormatList = ref(['xlsx', 'xls', 'csv', 'html', 'txt', 'json', 'rtf'] as Array<String>); // 支持的导出格式
const exportConfig = ref({
    withFilter: false, // 导出时是否携带筛选条件
    ext: "xlsx", // 所选导出格式
} as any);
const exportInfo = computed(() => {
    if (['xlsx', 'xls'].includes(exportConfig.value.ext)) {
        return null
    }
    let info = '建议选择 xlsx 或 xls 格式'
    let type = 'info'
    if (['rtf'].includes(exportConfig.value.ext)) {
        // 不推荐的导出格式
        info = '该格式易出现编码问题，' + info
        type = 'warning'
    } else if (['json'].includes(exportConfig.value.ext)) {
        info = '该格式方便程序读取，若您不了解该格式，建议选择其他格式进行导出'
        type = 'warning'
    }
    return { info, type }
})

// 获取表格数据
const getData = async () => {
    props.listFunc(query).then((data: any) => {
        console.log(data)

        // 深拷贝一份
        let _tableData = JSON.parse(JSON.stringify(data.list));
        // 字段映射
        fieldsMapper = data.fieldMapper;
        if (fieldsMapper) {
            // 对于每一个需要映射的字段
            for (let m of fieldsMapper) {
                // console.log("mapper", m)
                _tableData = _tableData.map((row: any) => {
                    let oldValue = row[m.key] // 取得旧值
                    let newValue = m.mapper[oldValue] // 翻译新值
                    row[m.value] = newValue // 塞回新值
                    return row
                })
            }
        }

        console.log("data.columns", data.columns)

        // 表格列
        tableFields.value = data.columns
            .filter((field: any) => field.fieldType != "null")
            .map((field: any) => {
                // query 填充默认空字符串
                if (typeof (query[field.field]) === "undefined") {
                    query[field.field] = ''
                }
                return { prop: field.prop, label: field.label, type: field.fieldType }
            });
        console.log("tableFields", tableFields.value)
        // 表格数据
        tableData.value = _tableData;
        // 总页数
        pageTotal.value = data.total;

        // id列列名
        idFieldName = data.idFieldName;
        console.log("idFieldName", idFieldName)

        // 筛选字段
        searchFields.value = data.columns
            .filter((field: any) => field.searchType != "null")
            .map((field: any) => {
                let f: any = {
                    placeholder: field.label,
                    field: field.field,
                    searchType: field.searchType,
                }
                switch (field.searchType) {
                    case 'select':
                        // 如果是下拉框，那么找到下拉框的 option 数据
                        f.options = fieldsMapper.find((field: any) => field.key == f.field).mapper
                        break;
                    default:
                        break;
                }
                return f
            });
        console.log("searchFields", searchFields.value);

        // 新增/修改弹窗列
        dialogFields.value = data.columns
            .map((field: any) => {
                let f: any = {
                    label: field.label,
                    field: field.field,
                    addType: field.addType,
                    addPlaceholder: field.addPlaceholder,
                    editType: field.editType,
                    editPlaceholder: field.editPlaceholder,
                    default: field.default,
                }
                switch (field.searchType) {
                    case 'select':
                        // 如果是下拉框，那么找到下拉框的 option 数据
                        f.options = fieldsMapper.find((field: any) => field.key == f.field).mapper
                        break;
                    default:
                        break;
                }
                return f
            });
        console.log("dialogFields", dialogFields.value);

        // 导出 excel 字段映射
        exportFields = data.columns
            .filter((field: any) => field.fieldType != "null")
            .map((field: any) => {
                return { field: field.field, label: field.label }
            });
        console.log("exportFields", exportFields)

        // 表单验证
        for (let field of data.columns) {
            rules.value[field.field] = field.validateRules
        }
        console.log("rules", rules.value)

        // 测试数据
        mockData = data.columns
            .filter((field: any) => field.mockRegex)
            .map((field: any) => {
                let spaceIndex = field.mockRegex.indexOf(' ')
                if (spaceIndex == -1) return
                let type = field.mockRegex.substring(0, spaceIndex)
                let str = field.mockRegex.substring(spaceIndex + 1)
                return {
                    canEdit: field.editType != "plainText",
                    field: field.field,
                    type: type,
                    str: str,
                }
            })
        console.log("mockData", mockData)
    })
};

// 查询按钮
const handleSearch = () => {
    query.pageIndex = 1;
    getData();
};

// 分页导航
const handlePageChange = (val: number) => {
    query.pageIndex = val;
    getData();
};

// 点击进入编辑框
const handleEdit = (index: number, row: any) => {
    idx = index;
    formId = row[idFieldName];
    form[idFieldName] = formId;
    // 遍历所有编辑项，并将 row 的值赋给表格
    for (let f of dialogFields.value) {
        switch (f.editType) {
            case "select":
            case "input-number":
                // 下拉框、数字输入框的值要为 string 类型
                form[f.field] = String(row[f.field]);
                break;
            default:
                form[f.field] = row[f.field];
                break;
        }
    }
    editVisible.value = true;
};

// 点击进入新建框
const handleNew = () => {
    idx = -1;
    formId = -1;
    form[idFieldName] = -1;
    // 遍历所有编辑项，并将 row 的值赋给表格
    for (let f of dialogFields.value) {
        switch (f.editType) {
            case "select":
            case "input-number":
                // 下拉框、数字输入框的值要为 string 类型
                form[f.field] = String(f.default);
                break;
            default:
                form[f.field] = f.default;
                break;
        }
    }
    editVisible.value = true;
};

// 编辑保存到数据库
const saveEdit = async (formEl: FormInstance | undefined) => {
    if (!formEl) return;
    console.log("formEl", formEl);
    formEl.validate(async (valid: boolean, invalidFields: any) => {
        // 表单校验
        if (!valid) {
            // console.log("invalidFields", invalidFields);
            // 对表单中的每一个不合法输入框进行遍历
            Object.values(invalidFields).forEach((input: any) => {
                // 对该不合法输入框的提示信息进行遍历
                input.forEach((element: any) => {
                    ElMessage.error({ message: element.message, grouping: true });
                });
            });
            return;
        }

        var result;
        if (formId > 0) {
            // 修改记录
            var result = await props.editFunc?.(form)
        } else {
            // 新增记录
            var result = await props.addFunc?.(form)
            query.pageIndex = Math.ceil((pageTotal.value + 1) / query.pageSize);
        }
        console.log("result", result)

        if (!result) {
            ElMessage.error({ message: "添加失败" });
            return
        }

        // 关闭弹窗
        editVisible.value = false;

        // 更新表格中数据
        await getData();

        ElMessage.success(formId > 0 ? `修改成功` : `添加成功`);
    });
};

// 删除操作
const handleDelete = (index: number, row: any) => {
    idx = index;
    // 二次确认删除
    ElMessageBox.confirm('确定要删除吗？', '提示', { type: 'warning' })
        .then(async () => {
            var result = await props.deleteFunc?.({
                id: row[idFieldName],
            })
            if (result) {
                ElMessage.success('删除成功');
                if (tableData.value.length == 1) {
                    // 删除了当前页面的最后一条记录，那么往前翻一页
                    if (query.pageIndex > 0) {
                        query.pageIndex -= 1;
                    }
                }
                tableData.value.splice(idx, 1);
                // 更新表格中数据
                await getData();
            } else {
                ElMessage.error('删除失败');
            }
        })
        .catch(() => { });
};

// 导出数据到文件
const handleExport = async () => {
    let params = {};
    // 如果选择仅导出满肚条件的数据，那么这里就填上查询参数
    if (exportConfig.value.withFilter) {
        params = JSON.parse(JSON.stringify(query))
        // 去除分页参数
        delete params["pageIndex"]
        delete params["pageSize"]
    }
    props.exportFunc(params).then((data: any) => {
        console.log("exportData", data)
        let sheetName = data.sheetName
        let fileName = data.fileName
        console.log("data.list", data.list)

        // 字段翻译
        let dataList = JSON.parse(JSON.stringify(data.list))
        if (fieldsMapper) {
            // 对于每一个需要映射的字段
            for (let m of fieldsMapper) {
                // console.log("mapper", m)
                dataList = dataList.map((row: any) => {
                    let oldValue = row[m.key] // 取得旧值
                    let newValue = m.mapper[oldValue] // 翻译新值
                    row[m.key] = newValue // 直接在旧 key 塞回新值
                    // 因为后续是通过翻译前的 key 拿的数据，所以这里不翻译 key 只翻译 value
                    return row
                })
            }
        }
        console.log("dataList", dataList)

        // 日期转换
        let timeField = tableFields.value.filter((field: any) => field.type == "time")
        console.log("timeField", timeField)
        for (let field of timeField) {
            console.log("field.prop", field.prop)
            dataList = dataList.map((row: any) => {
                row[field.prop] = new Date(row[field.prop] + 8 * 3600 * 1000).toISOString().replace('T', ' ').substring(0, 19)
                return row
            })
        }

        // 所有列的 field
        let fieldNameList = exportFields.map((f: any) => f.field)
        fieldNameList.unshift(idFieldName)

        // 第一行表头
        let firstRow = exportFields.map((f: any) => f.label)
        firstRow.unshift("ID")

        // 列宽
        let rowWidth = exportFields.map(() => { return { wch: 13 } })
        rowWidth.unshift({ wch: 5 })

        // 数据部分
        let excelList = dataList.map((row: any) => {
            // 通过翻译前的 key 拿数据
            return fieldNameList.map((field: any) => String(row[field] || ""))
        })
        excelList.unshift(firstRow) // 插入表头

        console.log("excelList", excelList)

        function downloadFile(text: string, fileName: string, contentType: string) {
            const blob = new Blob([text], { type: contentType });
            const href = URL.createObjectURL(blob);
            const alink = document.createElement("a");
            alink.style.display = "none";
            alink.download = fileName; // 下载后文件名
            alink.href = href;
            document.body.appendChild(alink);
            alink.click();
            document.body.removeChild(alink); // 下载完成移除元素
            URL.revokeObjectURL(href); // 释放掉blob对象
        }

        // 生成文件
        let _fileName: string = `${fileName || '数据导出'}.${exportConfig.value.ext || 'xlsx'}`
        if (exportConfig.value.ext === "json") {
            let text = JSON.stringify(excelList)
            downloadFile(text, _fileName, "application/json;charset=utf-8")
        } else if (exportConfig.value.ext === "txt") {
            let text = excelList.map((row: Array<any>) => row.join('\t')).join('\n')
            downloadFile(text, _fileName, "text/plain;charset=utf-8")
        } else {
            let worksheet = xlsx.utils.aoa_to_sheet(excelList)
            let workbook = xlsx.utils.book_new()
            worksheet["!cols"] = rowWidth; // 指定列宽
            xlsx.utils.book_append_sheet(workbook, worksheet, sheetName || 'Sheet1')
            xlsx.writeFile(workbook, _fileName)
        }

        ElMessage.success({ message: "导出成功" })
        exportFormVisible.value = false
    })
}

// 生成测试数据
const doMockData = (isEdit: boolean) => {
    for (let mock of mockData) {
        if (isEdit && !mock.canEdit) continue; // 跳过不允许修改的列，例如 用户名
        switch (mock.type) {
            case "DTD":
                let regexp = new RegExp(mock.str.substring(1, mock.str.length - 2))
                // console.log(regexp)
                form[mock.field] = Mock.mock({ 'regexp': regexp })['regexp']
                break;
            case "DPD":
                form[mock.field] = Mock.mock(mock.str)
                break;
            case "IMG":
                form[mock.field] = Mock.Random.dataImage(...mock.str.split(','))
                break;
        }
    }
}

// 网页加载完成后加载数据
onMounted(() => {
    getData();
});



// 附加内容
const dateTimePickerRangeShotcut = [
    {
        text: '今天',
        value: () => {
            const start = new Date()
            start.setHours(0)
            start.setMinutes(0)
            start.setSeconds(0)
            start.setMilliseconds(0)
            const end = new Date(start.getTime() + 24 * 3600 * 1000)
            return [start, end]
        },
    },
    {
        text: '昨天',
        value: () => {
            const end = new Date()
            end.setHours(0)
            end.setMinutes(0)
            end.setSeconds(0)
            end.setMilliseconds(0)
            const start = new Date(end.getTime() - 24 * 3600 * 1000)
            return [start, end]
        },
    },
    {
        text: '过去24小时',
        value: () => {
            const end = new Date()
            const start = new Date()
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 1)
            return [start, end]
        },
    },
    {
        text: '过去7天',
        value: () => {
            const end = new Date()
            const start = new Date()
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
            return [start, end]
        },
    },
    {
        text: '过去30天',
        value: () => {
            const end = new Date()
            const start = new Date()
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
            return [start, end]
        },
    },
    {
        text: '过去90天',
        value: () => {
            const end = new Date()
            const start = new Date()
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
            return [start, end]
        },
    },
]

</script>

<style scoped>
.handle-box {
    margin-bottom: 20px;
    line-height: 2.5em;
}

.handle-select {
    width: 150px;
}

.handle-input {
    width: 150px;
}

.table {
    width: 100%;
    font-size: 14px;
}

.red {
    color: #F56C6C;
}

.mr10 {
    margin-right: 10px;
}

.table-td-thumb {
    display: block;
    margin: auto;
    width: 40px;
    height: 40px;
}

.popup-item {
    width: 100%;
}

.oneLine {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
</style>
