<template>
    <div class="default-main">
        <div class="header-config-box">
            <el-row class="header-box">
                <div class="header">
                    <div class="header-item-box">
                        <FormItem
                            class="mr-20 table-name-item"
                            :label="t('crud.log.table_name')"
                            v-model="state.table.name"
                            type="string"
                            :placeholder="t('crud.crud.Name of the data table')"
                            :input-attr="{
                                onChange: onTableNameChange,
                            }"
                            :error="state.error.tableName"
                        />
                        <FormItem
                            class="table-comment-item"
                            :label="t('crud.crud.Data Table Notes')"
                            v-model="state.table.comment"
                            type="string"
                            :placeholder="t('crud.crud.For example: `user table` will be generated into `user management`')"
                        />
                    </div>
                    <div class="header-right">
                        <el-link v-if="crudState.type != 'create'" @click="state.showDesignChangeLog = true" class="design-change-log" type="primary">
                            {{ t('crud.crud.Table design change') }}
                        </el-link>
                        <el-button type="primary" :loading="state.loading.generate" @click="onGenerate" v-blur>
                            {{ t('crud.crud.Generate CRUD code') }}
                        </el-button>
                        <el-button @click="onAbandonDesign" type="danger" v-blur>{{ t('crud.crud.give up') }}</el-button>
                    </div>
                </div>
            </el-row>
            <transition :name="state.showHeaderSeniorConfig ? 'el-zoom-in-top' : 'el-zoom-in-bottom'">
                <div v-if="state.showHeaderSeniorConfig" class="header-senior-config-box">
                    <div class="header-senior-config-form">
                        <el-form-item :label-width="140" :label="t('crud.crud.Table Quick Search Fields')">
                            <el-select :clearable="true" :multiple="true" class="w100" v-model="state.table.quickSearchField" placement="bottom">
                                <el-option
                                    v-for="(item, idx) in state.fields"
                                    :key="idx + item.uuid!"
                                    :label="item.name + (item.comment ? '-' + item.comment : item.title)"
                                    :value="item.uuid!"
                                />
                            </el-select>
                        </el-form-item>
                        <div class="default-sort-field-box">
                            <el-form-item :label-width="140" class="default-sort-field mr-20" :label="t('crud.crud.Table Default Sort Fields')">
                                <el-select :clearable="true" v-model="state.table.defaultSortField" placement="bottom">
                                    <el-option
                                        v-for="(item, idx) in state.fields"
                                        :key="idx + item.uuid!"
                                        :label="item.name + (item.comment ? '-' + item.comment : item.title)"
                                        :value="item.uuid!"
                                    />
                                </el-select>
                            </el-form-item>
                            <FormItem
                                class="default-sort-field-type"
                                :label="t('crud.crud.sort order')"
                                v-model="state.table.defaultSortType"
                                type="select"
                                :input-attr="{
                                    content: { desc: t('crud.crud.sort order desc'), asc: t('crud.crud.sort order asc') },
                                }"
                            />
                        </div>
                        <el-form-item :label-width="140" :label="t('crud.crud.Fields as Table Columns')">
                            <el-select :clearable="true" :multiple="true" class="w100" v-model="state.table.columnFields" placement="bottom">
                                <el-option
                                    v-for="(item, idx) in state.fields"
                                    :key="idx + item.uuid!"
                                    :label="item.name + (item.comment ? '-' + item.comment : item.title)"
                                    :value="item.uuid!"
                                />
                            </el-select>
                        </el-form-item>
                        <el-form-item :label-width="140" :label="t('crud.crud.Fields as form items')">
                            <el-select :clearable="true" :multiple="true" class="w100" v-model="state.table.formFields" placement="bottom">
                                <el-option
                                    v-for="(item, idx) in state.fields"
                                    :key="idx + item.uuid!"
                                    :label="item.name + (item.comment ? '-' + item.comment : item.title)"
                                    :value="item.uuid!"
                                />
                            </el-select>
                        </el-form-item>
                        <FormItem
                            :label="t('crud.crud.The relative path to the generated code')"
                            v-model="state.table.generateRelativePath"
                            type="string"
                            :label-width="140"
                            :block-help="t('crud.crud.For quick combination code generation location, please fill in the relative path')"
                            :input-attr="{
                                onChange: onTableChange,
                                onInput: debouncedOnRelativePathInput,
                            }"
                        />
                        <FormItem
                            :label="t('crud.crud.Generated Controller Location')"
                            v-model="state.table.controllerFile"
                            type="string"
                            :label-width="140"
                        />
                        <el-form-item :label="t('crud.crud.Generated Data Model Location')" :label-width="140">
                            <el-input v-model="state.table.modelFile" type="string">
                                <template #append>
                                    <el-checkbox
                                        @change="onChangeCommonModel"
                                        v-model="state.table.isCommonModel"
                                        :label="t('crud.crud.Common model')"
                                        size="small"
                                        :true-value="1"
                                        :false-value="0"
                                    />
                                </template>
                            </el-input>
                        </el-form-item>
                        <FormItem
                            :label="t('crud.crud.Generated Validator Location')"
                            v-model="state.table.validateFile"
                            type="string"
                            :label-width="140"
                        />
                        <FormItem :label="t('crud.crud.WEB end view directory')" v-model="state.table.webViewsDir" type="string" :label-width="140" />
                        <FormItem
                            :label="t('Database connection')"
                            v-model="state.table.databaseConnection"
                            type="remoteSelect"
                            :label-width="140"
                            :block-help="t('Database connection help')"
                            :input-attr="{
                                pk: 'key',
                                field: 'key',
                                remoteUrl: getDatabaseConnectionListUrl,
                            }"
                        />
                    </div>
                </div>
            </transition>
            <div @click="state.showHeaderSeniorConfig = !state.showHeaderSeniorConfig" class="header-senior-config">
                <span>{{ t('crud.crud.Advanced Configuration') }}</span>
                <Icon
                    class="senior-config-arrow-icon"
                    size="14"
                    color="var(--el-text-color-primary)"
                    :name="state.showHeaderSeniorConfig ? 'el-icon-ArrowUp' : 'el-icon-ArrowDown'"
                />
            </div>
        </div>
        <el-row v-loading="state.loading.init" class="fields-box" :gutter="20">
            <el-col :xs="24" :span="6">
                <el-collapse class="field-collapse" v-model="state.fieldCollapseName">
                    <el-collapse-item :title="t('crud.crud.Common Fields')" name="common">
                        <div class="field-box" :ref="tabsRefs.set">
                            <div v-for="(field, index) in fieldItem.common" :key="index" class="field-item">
                                <span>{{ field.title }}</span>
                            </div>
                        </div>
                    </el-collapse-item>
                    <el-collapse-item :title="t('crud.crud.Base Fields')" name="base">
                        <div class="field-box" :ref="tabsRefs.set">
                            <div v-for="(field, index) in fieldItem.base" :key="index" class="field-item">
                                <span>{{ field.title }}</span>
                            </div>
                        </div>
                    </el-collapse-item>
                    <el-collapse-item :title="t('crud.crud.Advanced Fields')" name="senior">
                        <div class="field-box" :ref="tabsRefs.set">
                            <div v-for="(field, index) in fieldItem.senior" :key="index" class="field-item">
                                <span>{{ field.title }}</span>
                            </div>
                        </div>
                    </el-collapse-item>
                </el-collapse>
            </el-col>
            <el-col :xs="24" :span="12">
                <div ref="designWindowRef" class="design-window ba-scroll-style">
                    <div
                        v-for="(field, index) in state.fields"
                        :key="index"
                        :class="index === state.activateField ? 'activate' : ''"
                        @click="onActivateField(index)"
                        class="design-field-box"
                        :data-id="index"
                    >
                        <div class="design-field">
                            <span>{{ t('crud.crud.Field Name') }}：</span>
                            <BaInput
                                @pointerdown.stop
                                class="design-field-name-input"
                                :model-value="field.name"
                                type="string"
                                :attr="{
                                    size: 'small',
                                    onInput: ($event: string) => onFieldNameChange($event, index),
                                }"
                            />
                        </div>
                        <div class="design-field">
                            <span>{{ t('crud.crud.field comment') }}：</span>
                            <BaInput
                                @pointerdown.stop
                                class="design-field-name-comment"
                                v-model="field.comment"
                                type="string"
                                :attr="{
                                    size: 'small',
                                    onChange: onFieldCommentChange,
                                }"
                            />
                        </div>
                        <div class="design-field-right">
                            <el-button
                                v-if="['remoteSelect', 'remoteSelects'].includes(field.designType)"
                                @click.stop="onEditField(index, field)"
                                type="primary"
                                size="small"
                                v-blur
                                circle
                            >
                                <Icon color="var(--el-color-white)" size="15" name="fa fa-pencil icon" />
                            </el-button>
                            <el-button @click.stop="onDelField(index)" type="danger" size="small" v-blur circle>
                                <Icon color="var(--el-color-white)" size="15" name="fa fa-trash" />
                            </el-button>
                        </div>
                    </div>
                    <div class="design-field-empty" v-if="!state.fields.length && !state.draggingField">
                        {{ t('crud.crud.Drag the left element here to start designing CRUD') }}
                    </div>
                </div>
            </el-col>
            <el-col :xs="24" :span="6">
                <div class="field-config ba-scroll-style">
                    <div v-if="state.activateField === -1" class="design-field-empty">
                        {{ t('crud.crud.Please select a field from the left first') }}
                    </div>
                    <div v-else :key="'activate-field-' + state.activateField">
                        <el-form label-position="top">
                            <el-divider content-position="left">{{ t('crud.crud.Common') }}</el-divider>
                            <el-form-item :label="t('crud.crud.Generate type')">
                                <el-select
                                    @change="onFieldDesignTypeChange($event)"
                                    class="w100"
                                    :model-value="state.fields[state.activateField].designType"
                                    placement="bottom"
                                >
                                    <el-option v-for="(item, idx) in designTypes" :key="idx" :label="item.name" :value="idx" />
                                </el-select>
                            </el-form-item>
                            <FormItem
                                :label="t('crud.crud.Field comments (CRUD dictionary)')"
                                type="textarea"
                                :input-attr="{
                                    rows: 2,
                                    onChange: onFieldCommentChange,
                                }"
                                :placeholder="
                                    t(
                                        'crud.crud.The field comment will be used as the CRUD dictionary, and will be identified as the field title before the colon, and as the data dictionary after the colon'
                                    )
                                "
                                v-model="state.fields[state.activateField].comment"
                            />
                            <el-divider content-position="left">{{ t('crud.crud.Field Properties') }}</el-divider>
                            <FormItem
                                :label="t('crud.crud.Field Name')"
                                type="string"
                                :model-value="state.fields[state.activateField].name"
                                :input-attr="{
                                    onInput: ($event: string) => onFieldNameChange($event, state.activateField),
                                }"
                            />
                            <template v-if="state.fields[state.activateField].dataType">
                                <FormItem
                                    :label="t('crud.crud.Field Type')"
                                    :input-attr="{
                                        onChange: onFieldAttrChange,
                                    }"
                                    type="textarea"
                                    v-model="state.fields[state.activateField].dataType"
                                />
                            </template>
                            <template v-else>
                                <FormItem
                                    :label="t('crud.crud.Field Type')"
                                    :input-attr="{
                                        onChange: onFieldAttrChange,
                                    }"
                                    type="string"
                                    v-model="state.fields[state.activateField].type"
                                />
                                <div class="field-inline">
                                    <FormItem
                                        :label="t('crud.crud.length')"
                                        type="number"
                                        v-model="state.fields[state.activateField].length"
                                        :input-attr="{
                                            onChange: onFieldAttrChange,
                                        }"
                                    />
                                    <FormItem
                                        :label="t('crud.crud.decimal point')"
                                        type="number"
                                        v-model="state.fields[state.activateField].precision"
                                        :input-attr="{
                                            onChange: onFieldAttrChange,
                                        }"
                                    />
                                </div>
                            </template>
                            <el-form-item :label="t('crud.crud.Field Defaults')">
                                <el-select v-model="state.fields[state.activateField].defaultType">
                                    <el-option label="手动输入" value="INPUT" />
                                    <el-option label="EMPTY STRING（空字符串）" value="EMPTY STRING" />
                                    <el-option label="NULL" value="NULL" />
                                    <el-option label="无（不设默认值）" value="NONE" />
                                </el-select>
                                <el-input
                                    v-if="state.fields[state.activateField].defaultType == 'INPUT'"
                                    :placeholder="t('crud.crud.Please input the default value')"
                                    type="text"
                                    v-model="state.fields[state.activateField].default"
                                    @change="onFieldAttrChange"
                                    class="default-input"
                                />
                            </el-form-item>
                            <div class="field-inline">
                                <FormItem
                                    class="form-item-position-right"
                                    :label="t('crud.state.Primary key')"
                                    type="switch"
                                    v-model="state.fields[state.activateField].primaryKey"
                                    :input-attr="{
                                        onChange: onFieldAttrChange,
                                    }"
                                />
                                <FormItem
                                    class="form-item-position-right"
                                    :label="t('crud.crud.Auto increment')"
                                    type="switch"
                                    v-model="state.fields[state.activateField].autoIncrement"
                                    :input-attr="{
                                        onChange: onFieldAttrChange,
                                    }"
                                />
                            </div>
                            <div class="field-inline">
                                <FormItem
                                    class="form-item-position-right"
                                    :label="t('crud.crud.Unsigned')"
                                    type="switch"
                                    v-model="state.fields[state.activateField].unsigned"
                                    :input-attr="{
                                        onChange: onFieldAttrChange,
                                    }"
                                />
                                <FormItem
                                    class="form-item-position-right"
                                    :label="t('crud.crud.Allow NULL')"
                                    type="switch"
                                    v-model="state.fields[state.activateField].null"
                                    :input-attr="{
                                        onChange: onFieldAttrChange,
                                    }"
                                />
                            </div>
                            <template v-if="!isEmpty(state.fields[state.activateField].table)">
                                <el-divider content-position="left">{{ t('crud.crud.Field Table Properties') }}</el-divider>
                                <template v-for="(item, idx) in state.fields[state.activateField].table" :key="idx">
                                    <FormItem
                                        :label="$t('crud.crud.' + idx)"
                                        :type="item.type"
                                        v-model="state.fields[state.activateField].table[idx].value"
                                        :placeholder="state.fields[state.activateField].table[idx].placeholder ?? ''"
                                        :input-attr="{
                                            content: state.fields[state.activateField].table[idx].options ?? {},
                                            ...(state.fields[state.activateField].table[idx].attr ?? {}),
                                        }"
                                    />
                                </template>
                            </template>
                            <template v-if="!isEmpty(state.fields[state.activateField].form)">
                                <el-divider content-position="left">{{ t('crud.crud.Field Form Properties') }}</el-divider>
                                <template v-for="(item, idx) in state.fields[state.activateField].form" :key="idx">
                                    <FormItem
                                        v-if="item.type != 'hidden'"
                                        :label="$t('crud.crud.' + idx)"
                                        :type="item.type"
                                        v-model="state.fields[state.activateField].form[idx].value"
                                        :placeholder="state.fields[state.activateField].form[idx].placeholder ?? ''"
                                        :input-attr="{
                                            content: state.fields[state.activateField].form[idx].options ?? {},
                                            ...(state.fields[state.activateField].form[idx].attr ?? {}),
                                        }"
                                    />
                                </template>
                            </template>
                        </el-form>
                    </div>
                </div>
            </el-col>
        </el-row>
        <el-dialog
            @close="onCancelRemoteSelect"
            class="ba-operate-dialog"
            :model-value="state.remoteSelectPre.show"
            :title="t('crud.crud.Remote drop-down association information')"
            :close-on-click-modal="false"
            :destroy-on-close="true"
            @keyup.enter="onSaveRemoteSelect"
        >
            <el-scrollbar max-height="60vh">
                <div class="ba-operate-form" :style="'width: calc(100% - 80px)'">
                    <el-form
                        ref="formRef"
                        :model="state.remoteSelectPre.form"
                        :rules="remoteSelectPreFormRules"
                        v-loading="state.remoteSelectPre.loading"
                        label-position="right"
                        label-width="160px"
                        v-if="state.remoteSelectPre.index != -1 && state.fields[state.remoteSelectPre.index]"
                    >
                        <FormItem
                            :label="t('crud.crud.Associated Data Table')"
                            v-model="state.remoteSelectPre.form.table"
                            type="remoteSelect"
                            :key="state.table.databaseConnection"
                            :input-attr="{
                                pk: 'table',
                                field: 'comment',
                                params: {
                                    connection: state.table.databaseConnection,
                                    samePrefix: 1,
                                    excludeTable: [
                                        'area',
                                        'token',
                                        'captcha',
                                        'admin_group_access',
                                        'config',
                                        'admin_log',
                                        'user_money_log',
                                        'user_score_log',
                                    ],
                                },
                                remoteUrl: getTableListUrl,
                                onChange: onJoinTableChange,
                            }"
                            prop="table"
                        />
                        <div v-loading="state.loading.remoteSelect">
                            <FormItem
                                prop="pk"
                                type="select"
                                :label="t('crud.crud.Drop down value field')"
                                v-model="state.remoteSelectPre.form.pk"
                                :placeholder="t('crud.crud.Please select the value field of the select component')"
                                :key="'select-value' + JSON.stringify(state.remoteSelectPre.fieldList)"
                                :input-attr="{
                                    content: state.remoteSelectPre.fieldList,
                                }"
                            />
                            <FormItem
                                prop="label"
                                type="select"
                                :label="t('crud.crud.Drop down label field')"
                                v-model="state.remoteSelectPre.form.label"
                                :placeholder="t('crud.crud.Please select the label field of the select component')"
                                :key="'select-label' + JSON.stringify(state.remoteSelectPre.fieldList)"
                                :input-attr="{
                                    content: state.remoteSelectPre.fieldList,
                                }"
                            />
                            <FormItem
                                v-if="state.fields[state.remoteSelectPre.index].designType == 'remoteSelect'"
                                prop="joinField"
                                type="selects"
                                :label="t('crud.crud.Fields displayed in the table')"
                                v-model="state.remoteSelectPre.form.joinField"
                                :placeholder="t('crud.crud.Please select the fields displayed in the table')"
                                :key="'join-field' + JSON.stringify(state.remoteSelectPre.fieldList)"
                                :input-attr="{
                                    content: state.remoteSelectPre.fieldList,
                                }"
                            />
                            <FormItem
                                :label="t('crud.crud.Data source configuration type')"
                                v-model="state.remoteSelectPre.form.sourceConfigType"
                                type="radio"
                                :input-attr="{
                                    border: true,
                                    content: {
                                        crud: t('crud.crud.Fast configuration with generated controllers and models'),
                                        custom: t('crud.crud.Custom configuration'),
                                    },
                                }"
                            />
                            <FormItem
                                v-if="state.remoteSelectPre.form.sourceConfigType == 'crud'"
                                prop="controllerFile"
                                type="select"
                                :label="t('crud.crud.Controller position')"
                                v-model="state.remoteSelectPre.form.controllerFile"
                                :placeholder="t('crud.crud.Please select the controller of the data table')"
                                :key="'controller-file' + JSON.stringify(state.remoteSelectPre.controllerFileList)"
                                :input-attr="{
                                    content: state.remoteSelectPre.controllerFileList,
                                }"
                                :block-help="
                                    t(
                                        'crud.crud.The remote pull-down will request the corresponding controller to obtain data, so it is recommended that you create the CRUD of the associated table'
                                    )
                                "
                            />

                            <!-- 数据源配置类型为CRUD时，模型位置必填 -->
                            <FormItem
                                :prop="state.remoteSelectPre.form.sourceConfigType == 'crud' ? 'modelFile' : ''"
                                type="select"
                                :label="t('crud.crud.Data Model Location')"
                                v-model="state.remoteSelectPre.form.modelFile"
                                :placeholder="t('crud.crud.Please select the data model location of the data table')"
                                :key="'model-file' + JSON.stringify(state.remoteSelectPre.modelFileList)"
                                :input-attr="{
                                    content: state.remoteSelectPre.modelFileList,
                                }"
                                :block-help="
                                    state.remoteSelectPre.form.sourceConfigType == 'crud'
                                        ? ''
                                        : t(
                                              'crud.crud.If it is left blank, the model of the associated table will be generated automatically If the table already has a model, it is recommended to select it to avoid repeated generation'
                                          )
                                "
                            />
                            <el-form-item
                                v-if="state.table.databaseConnection && state.remoteSelectPre.form.modelFile"
                                :label="t('Database connection')"
                            >
                                <el-text size="large" type="danger">{{ state.table.databaseConnection }}</el-text>
                                <div class="block-help">
                                    <div>{{ t('crud.crud.Check model class', { connection: state.table.databaseConnection }) }}</div>
                                    <div>{{ t('crud.crud.There is no connection attribute in model class') }}</div>
                                </div>
                            </el-form-item>
                            <FormItem
                                v-if="state.remoteSelectPre.form.sourceConfigType == 'custom'"
                                prop="remoteUrl"
                                :label="t('crud.crud.api url')"
                                type="string"
                                v-model="state.remoteSelectPre.form.remoteUrl"
                                :placeholder="t('crud.crud.api url example')"
                            />
                            <FormItem
                                v-if="state.remoteSelectPre.form.sourceConfigType == 'custom'"
                                :label="t('crud.crud.remote-primary-table-alias')"
                                type="string"
                                v-model="state.remoteSelectPre.form.primaryTableAlias"
                                :block-help="
                                    t(
                                        'crud.crud.If the remote interface query involves associated query of multiple tables, enter the alias of the primary data table here'
                                    )
                                "
                            >
                                <template #append>.{{ state.remoteSelectPre.form.pk }}</template>
                            </FormItem>
                            <el-form-item :label="t('Reminder')">
                                <div class="block-help">
                                    {{ t('crud.crud.Design remote select tips') }}
                                </div>
                            </el-form-item>
                        </div>
                    </el-form>
                </div>
            </el-scrollbar>
            <template #footer>
                <div :style="'width: calc(100% - 88px)'">
                    <el-button @click="onCancelRemoteSelect">{{ $t('Cancel') }}</el-button>
                    <el-button v-blur @click="onSaveRemoteSelect" type="primary">
                        {{ $t('Save') }}
                    </el-button>
                </div>
            </template>
        </el-dialog>
        <el-dialog
            @close="closeConfirmGenerate"
            class="ba-operate-dialog confirm-generate-dialog"
            :model-value="state.confirmGenerate.show"
            :title="t('crud.crud.Confirm CRUD code generation')"
        >
            <div class="confirm-generate-dialog-body">
                <el-alert
                    v-if="state.confirmGenerate.controller"
                    :title="t('crud.crud.The controller already exists Continuing to generate will automatically overwrite the existing code!')"
                    center
                    type="error"
                />
                <el-alert
                    v-if="showTableConflictConfirmGenerate()"
                    :title="
                        t(
                            'crud.crud.The data table already exists Continuing to generate will automatically delete the original table and create a new one!'
                        )
                    "
                    class="mt-10"
                    center
                    type="error"
                />
                <el-alert
                    v-if="state.confirmGenerate.menu"
                    :title="
                        t(
                            'crud.crud.The menu rule with the same name already exists The menu and permission node will not be created in this generation'
                        )
                    "
                    class="mt-10"
                    center
                    type="error"
                />
            </div>
            <template #footer>
                <div class="confirm-generate-dialog-footer">
                    <el-button @click="closeConfirmGenerate">{{ $t('Cancel') }}</el-button>
                    <el-button :loading="state.loading.generate" v-blur @click="startGenerate" type="primary">
                        {{ t('crud.crud.Continue building') }}
                    </el-button>
                </div>
            </template>
        </el-dialog>
        <el-dialog class="ba-operate-dialog design-change-log-dialog" width="20%" v-model="state.showDesignChangeLog">
            <template #header>
                <div v-drag="['.design-change-log-dialog', '.el-dialog__header']">
                    {{ t('crud.crud.Data table design changes preview') }}
                </div>
            </template>
            <el-scrollbar max-height="400px">
                <template v-if="state.table.designChange.length">
                    <el-timeline class="design-change-log-timeline">
                        <el-timeline-item
                            v-for="(item, idx) in state.table.designChange"
                            :key="idx"
                            :type="getTableDesignTimelineType(item.type)"
                            :hollow="true"
                            :hide-timestamp="true"
                        >
                            <div class="design-timeline-box">
                                <el-checkbox v-model="item.sync" :label="getTableDesignChangeContent(item)" size="small" />
                            </div>
                        </el-timeline-item>
                    </el-timeline>
                    <span class="design-change-tips">{{ t('crud.crud.designChangeTips') }}</span>
                </template>
                <div class="design-change-tips" v-else>暂无表设计变更</div>
                <FormItem
                    :label="t('crud.crud.tableReBuild')"
                    class="rebuild-form-item"
                    v-model="state.table.rebuild"
                    type="radio"
                    :input-attr="{
                        border: true,
                        content: { No: t('crud.crud.No'), Yes: t('crud.crud.Yes') },
                    }"
                    :block-help="t('crud.crud.tableReBuildBlockHelp')"
                />
            </el-scrollbar>
            <template #footer>
                <div class="confirm-generate-dialog-footer">
                    <el-button @click="state.showDesignChangeLog = false">
                        {{ t('Confirm') }}
                    </el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>

<script setup lang="ts">
import { useTemplateRefsList } from '@vueuse/core'
import type { FormItemRule, MessageHandler, TimelineItemProps } from 'element-plus'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import { cloneDeep, debounce, isEmpty, range } from 'lodash-es'
import type { SortableEvent } from 'sortablejs'
import Sortable from 'sortablejs'
import { nextTick, onMounted, reactive, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { generate, generateCheck, getFileData, parseFieldData, postLogStart, uploadCompleted, uploadLog } from '/@/api/backend/crud'
import { getDatabaseConnectionListUrl, getTableFieldList, getTableListUrl } from '/@/api/common'
import BaInput from '/@/components/baInput/index.vue'
import FormItem from '/@/components/formItem/index.vue'
import { useConfig } from '/@/stores/config'
import { useTerminal } from '/@/stores/terminal'
import { getArrayKey } from '/@/utils/common'
import { uuid } from '/@/utils/random'
import { buildValidatorData, regularVarName } from '/@/utils/validate'
import { reloadServer } from '/@/utils/vite'
import type { FieldItem, TableDesignChange, TableDesignChangeType } from '/@/views/backend/crud/index'
import { changeStep, state as crudState, designTypes, fieldItem, getTableAttr, tableFieldsKey } from '/@/views/backend/crud/index'

let nameRepeatCount = 1
const { t } = useI18n()
const config = useConfig()
const terminal = useTerminal()
const formRef = useTemplateRef('formRef')
const tabsRefs = useTemplateRefsList<HTMLElement>()
const designWindowRef = useTemplateRef('designWindowRef')

const state: {
    loading: {
        init: boolean
        generate: boolean
        remoteSelect: boolean
    }
    sync: number
    table: {
        name: string
        comment: string
        quickSearchField: string[]
        defaultSortField: string
        formFields: string[]
        columnFields: string[]
        defaultSortType: string
        generateRelativePath: string
        isCommonModel: number
        modelFile: string
        controllerFile: string
        validateFile: string
        webViewsDir: string
        databaseConnection: string
        designChange: TableDesignChange[]
        rebuild: string
    }
    fields: FieldItem[]
    activateField: number
    fieldCollapseName: string[]
    remoteSelectPre: {
        show: boolean
        index: number
        fieldList: anyObj
        modelFileList: anyObj
        controllerFileList: anyObj
        loading: boolean
        hideDelField: boolean
        form: {
            table: string
            pk: string
            label: string
            joinField: string[]
            sourceConfigType: 'crud' | 'custom'
            remoteUrl: string
            modelFile: string
            controllerFile: string
            primaryTableAlias: string
        }
    }
    showHeaderSeniorConfig: boolean
    confirmGenerate: {
        show: boolean
        menu: boolean
        table: boolean
        controller: boolean
    }
    draggingField: boolean
    showDesignChangeLog: boolean
    error: {
        tableName: string
        fieldName: MessageHandler | null
        fieldNameDuplication: MessageHandler | null
    }
} = reactive({
    loading: {
        init: false,
        generate: false,
        remoteSelect: false,
    },
    sync: 0,
    table: {
        name: '',
        comment: '',
        quickSearchField: [],
        defaultSortField: '',
        formFields: [],
        columnFields: [],
        defaultSortType: 'desc',
        generateRelativePath: '',
        isCommonModel: 0,
        modelFile: '',
        controllerFile: '',
        validateFile: '',
        webViewsDir: '',
        databaseConnection: '',
        designChange: [],
        rebuild: 'No',
    },
    fields: [],
    activateField: -1,
    fieldCollapseName: ['common', 'base', 'senior'],
    remoteSelectPre: {
        show: false,
        index: -1,
        fieldList: [],
        modelFileList: [],
        controllerFileList: [],
        loading: false,
        hideDelField: false,
        form: {
            table: '',
            pk: '',
            label: '',
            joinField: [],
            sourceConfigType: 'crud',
            remoteUrl: '',
            modelFile: '',
            controllerFile: '',
            primaryTableAlias: '',
        },
    },
    showHeaderSeniorConfig: true,
    confirmGenerate: {
        show: false,
        menu: false,
        table: false,
        controller: false,
    },
    draggingField: false,
    showDesignChangeLog: false,
    error: {
        tableName: '',
        fieldName: null,
        fieldNameDuplication: null,
    },
})

type TableKey = keyof typeof state.table

const onActivateField = (idx: number) => {
    state.activateField = idx
}

const onFieldDesignTypeChange = (designType: string) => {
    // 获取新的类型的数据
    let fieldDesignData: FieldItem | null = null
    for (const key in fieldItem) {
        const fieldItemIndex = getArrayKey(fieldItem[key as keyof typeof fieldItem], 'designType', designType)
        if (fieldItemIndex !== false) {
            fieldDesignData = cloneDeep(fieldItem[key as keyof typeof fieldItem][fieldItemIndex])
            break
        }
    }

    if (!fieldDesignData) return false

    // 主键重复检查
    if (!primaryKeyRepeatCheck(fieldDesignData, state.activateField)) {
        return false
    }

    // 选中字段数据
    const field = cloneDeep(state.fields[state.activateField])

    // 赋值新类型
    field.designType = designType

    // 保留字段的 table 和 form 数据，此处额外处理以便交付给 handleFieldAttr 函数
    for (const tKey in field.table) {
        field.table[tKey] = field.table[tKey].value
    }
    for (const tKey in field.form) {
        field.form[tKey] = field.form[tKey].value
    }
    state.fields[state.activateField] = handleFieldAttr(field)

    // 保留字段的 uuid
    state.fields[state.activateField].uuid = field.uuid

    // 询问是否切换至预设方案（除了字段名的属性全部重置）
    ElMessageBox.confirm(t('crud.crud.Reset generate type attr'), t('Reminder'), {
        confirmButtonText: t('Confirm') + t('Reset'),
        cancelButtonText: t('crud.crud.Design efficiency'),
        type: 'warning',
        closeOnClickModal: false,
    })
        .then(() => {
            // 记录字段属性更新
            onFieldAttrChange()

            // 删除快速搜索和排序，根据新类型重新赋值
            clearFieldTableData(state.fields[state.activateField].uuid!)

            // 重置属性，除了 name
            const oldName = state.fields[state.activateField].name
            state.fields[state.activateField] = handleFieldAttr(fieldDesignData)
            state.fields[state.activateField].name = oldName

            if (fieldDesignData.primaryKey) {
                // 设置为默认排序字段、快速搜索字段
                state.table.quickSearchField.push(state.fields[state.activateField].uuid!)
                if (!state.table.defaultSortField) {
                    state.table.defaultSortField = state.fields[state.activateField].uuid!
                }
            }

            if (fieldDesignData.designType == 'weigh') {
                state.table.defaultSortField = state.fields[state.activateField].uuid!
            }

            // 远程下拉参数预填
            if (['remoteSelect', 'remoteSelects'].includes(fieldDesignData.designType)) {
                showRemoteSelectPre(state.activateField, true)
            }

            // 表单表格字段预定义
            if (!fieldDesignData.formBuildExclude) {
                state.table.formFields.push(state.fields[state.activateField].uuid!)
            }
            if (!fieldDesignData.tableBuildExclude) {
                state.table.columnFields.push(state.fields[state.activateField].uuid!)
            }
        })
        .catch(() => {})
}

/**
 * 字段名修改
 */
const onFieldNameChange = (val: string, index: number) => {
    const oldName = state.fields[index].name
    state.fields[index].name = val
    logTableDesignChange({
        type: 'change-field-name',
        index: state.activateField,
        oldName: oldName,
        newName: val,
    })
}

/**
 * 主键字段重复检测
 */
const primaryKeyRepeatCheck = (field: FieldItem, excludeIndex: number = -1) => {
    if (field.primaryKey === true) {
        const primaryKeyField = state.fields.find((item, index) => {
            if (excludeIndex > -1 && index == excludeIndex) {
                return false
            }
            return item.primaryKey
        })
        if (primaryKeyField) {
            ElNotification({
                type: 'error',
                message: t('crud.crud.There can only be one primary key field'),
            })
            return false
        }
    }
    return true
}

/**
 * 全部字段的名称命名规则检测
 */
const fieldNameCheck = (showErrorType: 'ElNotification' | 'ElMessage') => {
    if (state.error.fieldName) {
        state.error.fieldName.close()
        state.error.fieldName = null
    }
    for (const key in state.fields) {
        if (!regularVarName(state.fields[key].name)) {
            let msg = t(
                'crud.crud.Field name is invalid It starts with a letter or underscore and cannot contain any character other than letters, digits, or underscores',
                { field: state.fields[key].name }
            )
            if (showErrorType == 'ElMessage') {
                state.error.fieldName = ElMessage({
                    message: msg,
                    type: 'error',
                    duration: 0,
                })
            } else {
                ElNotification({
                    type: 'error',
                    message: msg,
                })
            }
            return false
        }
    }
    return true
}

/**
 * 全部字段的名称重复检测
 */
const fieldNameDuplicationCheck = (showErrorType: 'ElNotification' | 'ElMessage') => {
    if (state.error.fieldNameDuplication) {
        state.error.fieldNameDuplication.close()
        state.error.fieldNameDuplication = null
    }
    for (const key in state.fields) {
        let count = 0
        for (const checkKey in state.fields) {
            if (state.fields[key].name == state.fields[checkKey].name) {
                count++
            }
            if (count > 1) {
                let msg = t('crud.crud.Field name duplication', { field: state.fields[key].name })
                if (showErrorType == 'ElMessage') {
                    state.error.fieldNameDuplication = ElMessage({
                        message: msg,
                        type: 'error',
                        duration: 0,
                    })
                } else {
                    ElNotification({
                        type: 'error',
                        message: msg,
                    })
                }
                return false
            }
        }
    }
    return true
}

const onFieldAttrChange = () => {
    logTableDesignChange({
        type: 'change-field-attr',
        index: state.activateField,
        oldName: state.fields[state.activateField].name,
        newName: '',
    })
}

/**
 * 从 state.table.* 清理某个字段的数据
 */
const clearFieldTableData = (uuid: string) => {
    if (uuid == state.table.defaultSortField) {
        state.table.defaultSortField = ''
    }

    for (const key in tableFieldsKey) {
        const delIdx = (state.table[tableFieldsKey[key] as TableKey] as string[]).findIndex((item) => {
            return item == uuid
        })
        if (delIdx != -1) {
            ;(state.table[tableFieldsKey[key] as TableKey] as string[]).splice(delIdx, 1)
        }
    }
}

const onDelField = (index: number) => {
    if (!state.fields[index]) return
    state.activateField = -1

    clearFieldTableData(state.fields[index].uuid!)

    logTableDesignChange({
        type: 'del-field',
        oldName: state.fields[index].name,
        newName: '',
    })

    // 删除权重字段时，重设默认排序字段
    if (state.fields[index].designType == 'weigh') {
        const pkField = state.fields.find((item) => {
            return ['pk', 'spk'].includes(item.designType)
        })
        if (pkField) {
            state.table.defaultSortField = pkField.uuid!
        }
    }

    state.fields.splice(index, 1)
}

const showRemoteSelectPre = (index: number, hideDelField = false) => {
    state.remoteSelectPre.show = true
    state.remoteSelectPre.loading = true
    state.remoteSelectPre.index = index
    state.remoteSelectPre.hideDelField = hideDelField

    if (state.fields[index] && state.fields[index].form['remote-table'].value) {
        state.remoteSelectPre.form.table = state.fields[index].form['remote-table'].value
        state.remoteSelectPre.form.pk = state.fields[index].form['remote-pk'].value
        state.remoteSelectPre.form.label = state.fields[index].form['remote-field'].value
        state.remoteSelectPre.form.controllerFile = state.fields[index].form['remote-controller'].value
        state.remoteSelectPre.form.modelFile = state.fields[index].form['remote-model'].value
        state.remoteSelectPre.form.remoteUrl = state.fields[index].form['remote-url'].value
        state.remoteSelectPre.form.sourceConfigType = state.fields[index].form['remote-source-config-type'].value
        state.remoteSelectPre.form.primaryTableAlias = state.fields[index].form['remote-primary-table-alias'].value
        state.remoteSelectPre.form.joinField = state.fields[index].form['relation-fields'].value.split(',')
        getTableFieldList(state.fields[index].form['remote-table'].value, true, state.table.databaseConnection).then((res) => {
            const fieldSelect: anyObj = {}
            for (const key in res.data.fieldList) {
                fieldSelect[key] = (key ? key + ' - ' : '') + res.data.fieldList[key]
            }
            state.remoteSelectPre.fieldList = fieldSelect
        })
        if (isEmpty(state.remoteSelectPre.modelFileList) || isEmpty(state.remoteSelectPre.controllerFileList)) {
            getFileData(state.fields[index].form['remote-table'].value).then((res) => {
                state.remoteSelectPre.modelFileList = res.data.modelFileList
                state.remoteSelectPre.controllerFileList = res.data.controllerFileList
            })
        }
    }

    state.remoteSelectPre.loading = false
}

const onEditField = (index: number, field: FieldItem) => {
    if (['remoteSelect', 'remoteSelects'].includes(field.designType)) return showRemoteSelectPre(index)
}

const closeConfirmGenerate = () => {
    state.confirmGenerate.show = false
}

const startGenerate = () => {
    state.loading.generate = true

    // 简化设计字段数据
    const fields = cloneDeep(state.fields)
    for (const key in fields) {
        for (const tKey in fields[key].table) {
            fields[key].table[tKey] = fields[key].table[tKey].value
        }
        for (const tKey in fields[key].form) {
            fields[key].form[tKey] = fields[key].form[tKey].value
        }
    }

    // 通过 uuid 获取字段 name
    const table = cloneDeep(state.table)
    if (table.defaultSortField) {
        const defaultSortFieldIndex = getArrayKey(state.fields, 'uuid', table.defaultSortField)
        if (defaultSortFieldIndex !== false) {
            table.defaultSortField = state.fields[defaultSortFieldIndex].name
        }
    }
    for (const key in tableFieldsKey) {
        const names: string[] = []
        const uuids = table[tableFieldsKey[key] as TableKey] as string[]
        for (const uKey in uuids) {
            const uuidFieldIndex = getArrayKey(state.fields, 'uuid', uuids[uKey])
            if (uuidFieldIndex !== false) {
                names.push(state.fields[uuidFieldIndex].name)
            }
        }

        ;(table[tableFieldsKey[key] as TableKey] as string[]) = names
    }

    generate({
        type: crudState.type,
        table,
        fields,
    })
        .then((res) => {
            const callback = () => {
                const webViewsDir = state.table.webViewsDir.replace(/^web/, '.')
                terminal.toggle(true)
                terminal.addTask('npx.prettier', false, webViewsDir, () => {
                    terminal.toggle(false)
                    terminal.toggleDot(true)
                    nextTick(() => {
                        // 要求 Vite 服务端重启
                        if (import.meta.hot) {
                            reloadServer('crud')
                        } else {
                            ElNotification({
                                type: 'error',
                                message: t('crud.crud.Vite hot warning'),
                            })
                        }
                    })
                })
            }

            if ((state.sync > 0 && config.crud.syncedUpdate === 'yes') || (state.sync == 0 && config.crud.syncType == 'automatic')) {
                uploadLog({
                    logs: [
                        {
                            ...res.data.crudLog,
                            public: config.crud.syncAutoPublic === 'yes' ? 1 : 0,
                            newLog: 1,
                        },
                    ],
                    save: 1,
                })
                    .then((res) => {
                        uploadCompleted({ syncIds: res.data.syncIds }).finally(() => {
                            callback()
                        })
                    })
                    .catch(() => {
                        callback()
                    })
            } else {
                callback()
            }
        })
        .finally(() => {
            state.loading.generate = false
            closeConfirmGenerate()
        })
}

const onGenerate = () => {
    // 字段名称检查
    if (!fieldNameCheck('ElNotification')) return
    if (!fieldNameDuplicationCheck('ElNotification')) return

    let msg = ''

    // 主键检查
    const pkIndex = state.fields.findIndex((item) => {
        return item.primaryKey
    })
    if (pkIndex === -1) {
        msg = t('crud.crud.Please design the primary key field!')
    }

    // 表名检查
    if (!state.table.name) msg = t('crud.crud.Please enter the data table name!')
    if (state.error.tableName) msg = t('crud.crud.Please enter the correct table name!')

    if (msg) {
        ElNotification({
            type: 'error',
            message: msg,
        })
        return
    }

    state.loading.generate = true
    generateCheck({
        table: state.table.name,
        connection: state.table.databaseConnection,
        webViewsDir: state.table.webViewsDir,
        controllerFile: state.table.controllerFile,
    })
        .then(() => {
            startGenerate()
        })
        .catch((res) => {
            state.loading.generate = false
            if (res.code == -1) {
                state.confirmGenerate.menu = res.data.menu
                state.confirmGenerate.table = res.data.table
                state.confirmGenerate.controller = res.data.controller
                if (showTableConflictConfirmGenerate() || state.confirmGenerate.controller || state.confirmGenerate.menu) {
                    state.confirmGenerate.show = true
                } else {
                    startGenerate()
                }
            } else {
                ElNotification({
                    type: 'error',
                    message: res.msg,
                })
            }
        })
}

const showTableConflictConfirmGenerate = () => state.confirmGenerate.table && (crudState.type == 'create' || state.table.rebuild == 'Yes')

const onAbandonDesign = () => {
    if (!state.table.name && !state.table.comment && !state.fields.length) {
        return changeStep('start')
    }
    ElMessageBox.confirm(t('crud.crud.It is irreversible to give up the design Are you sure you want to give up?'), t('Reminder'), {
        confirmButtonText: t('crud.crud.give up'),
        cancelButtonText: t('Cancel'),
        type: 'warning',
    })
        .then(() => {
            changeStep('start')
        })
        .catch(() => {})
}

interface SortableEvt extends SortableEvent {
    originalEvent?: DragEvent
}

/**
 * 处理字段的属性
 */
const handleFieldAttr = (field: FieldItem) => {
    field = cloneDeep(field)
    const designTypeAttr = cloneDeep(designTypes[field.designType])
    for (const tKey in field.form) {
        if (designTypeAttr.form[tKey]) designTypeAttr.form[tKey].value = field.form[tKey]
        if (tKey == 'image-multi' && field.form[tKey]) {
            designTypeAttr.table['render'] = getTableAttr('render', 'images')
        }
    }
    for (const tKey in field.table) {
        if (designTypeAttr.table[tKey]) designTypeAttr.table[tKey].value = field.table[tKey]
    }
    field.form = designTypeAttr.form
    field.table = designTypeAttr.table
    field.uuid = uuid()
    return field
}

/**
 * 根据字段字典重新生成字段的数据类型
 */
const onFieldCommentChange = (comment: string) => {
    onFieldAttrChange()
    if (['enum', 'set'].includes(state.fields[state.activateField].type)) {
        if (!comment) {
            state.fields[state.activateField].dataType = `${state.fields[state.activateField].type}()`
            return
        }
        comment = comment.replaceAll('：', ':')
        comment = comment.replaceAll('，', ',')
        let comments = comment.split(':')
        if (comments[1]) {
            comments = comments[1].split(',')
            comments = comments
                .map((value) => {
                    if (!value) return ''
                    let temp = value.split('=')
                    if (temp[0] && temp[1]) {
                        return `'${temp[0]}'`
                    }
                    return ''
                })
                .filter((str: string) => str != '')

            // 字段数据类型
            state.fields[state.activateField].dataType = `${state.fields[state.activateField].type}(${comments.join(',')})`
        }
    }
}

const loadData = () => {
    tableDesignChangeInit()
    if (!['db', 'sql', 'log'].includes(crudState.type)) return

    state.loading.init = true

    // 从历史记录开始
    if (crudState.type == 'log') {
        postLogStart(crudState.startData.logId, crudState.startData.logType)
            .then((res) => {
                // 字段数据
                const fields = res.data.fields
                for (const key in fields) {
                    const field = handleFieldAttr(fields[key])

                    // 默认值和默认值类型分析
                    if (typeof field.defaultType == 'undefined') {
                        if (field.default && ['none', 'null', 'empty string'].includes(field.default)) {
                            field.defaultType = field.default.toUpperCase() as 'EMPTY STRING' | 'NULL' | 'NONE'
                            field.default = ''
                        } else {
                            field.defaultType = 'INPUT'
                        }
                    }

                    state.fields.push(field)
                }

                // 表数据
                if (res.data.table.defaultSortField) {
                    const defaultSortFieldNameIndex = getArrayKey(state.fields, 'name', res.data.table.defaultSortField)
                    if (defaultSortFieldNameIndex !== false) {
                        res.data.table.defaultSortField = state.fields[defaultSortFieldNameIndex].uuid!
                    }
                }
                for (const key in tableFieldsKey) {
                    const uuids: string[] = []
                    const names = res.data.table[tableFieldsKey[key] as TableKey] as string[]
                    for (const nKey in names) {
                        const nameFieldIndex = getArrayKey(state.fields, 'name', names[nKey])
                        if (nameFieldIndex !== false) {
                            uuids.push(state.fields[nameFieldIndex].uuid!)
                        }
                    }

                    ;(res.data.table[tableFieldsKey[key] as TableKey] as string[]) = uuids
                }

                state.sync = res.data.sync
                state.table = res.data.table
                tableDesignChangeInit()
                if (res.data.table.empty) {
                    state.table.rebuild = 'Yes'
                }
                state.table.isCommonModel = parseInt(res.data.table.isCommonModel)
                state.table.databaseConnection = res.data.table.databaseConnection ? res.data.table.databaseConnection : ''
            })
            .finally(() => {
                state.loading.init = false
            })
        return
    }

    // 从数据表或sql开始
    parseFieldData({
        type: crudState.type,
        table: crudState.startData.table,
        sql: crudState.startData.sql,
        connection: crudState.startData.databaseConnection,
    })
        .then((res) => {
            let fields = []
            for (const key in res.data.columns) {
                const field = handleFieldAttr(res.data.columns[key])
                if (!['id', 'update_time', 'create_time', 'updatetime', 'createtime'].includes(field.name)) {
                    state.table.formFields.push(field.uuid!)
                }
                if (!['textarea', 'file', 'files', 'editor', 'password', 'array'].includes(field.designType)) {
                    state.table.columnFields.push(field.uuid!)
                }
                if (field.designType == 'pk') {
                    state.table.defaultSortField = field.uuid!
                    state.table.quickSearchField.push(field.uuid!)
                }
                if (field.designType == 'weigh') {
                    state.table.defaultSortField = field.uuid!
                }
                fields.push(field)
            }
            state.fields = fields
            state.table.comment = res.data.comment
            state.table.databaseConnection = crudState.startData.databaseConnection
            if (res.data.empty) {
                state.table.rebuild = 'Yes'
            }
            if (crudState.type == 'db' && crudState.startData.table) {
                state.table.name = crudState.startData.table
                onTableChange(crudState.startData.table)
            }
        })
        .finally(() => {
            state.loading.init = false
        })
}

/**
 * 字段名称重复时自动重命名
 */
const autoRenameRepeatField = (fieldName: string) => {
    const nameRepeatKey = getArrayKey(state.fields, 'name', fieldName)
    if (nameRepeatKey !== false) {
        fieldName += nameRepeatCount
        nameRepeatCount++
        return autoRenameRepeatField(fieldName)
    } else {
        return fieldName
    }
}

onMounted(() => {
    loadData()
    const sortable = Sortable.create(designWindowRef.value!, {
        group: 'design-field',
        animation: 200,
        filter: '.design-field-empty',
        onAdd: (evt: SortableEvt) => {
            const name = evt.originalEvent?.dataTransfer?.getData('name')
            const field = fieldItem[name as keyof typeof fieldItem]
            if (field && field[evt.oldIndex!]) {
                const data = handleFieldAttr(field[evt.oldIndex!])

                // 主键重复检测
                if (data.primaryKey) {
                    if (primaryKeyRepeatCheck(data)) {
                        // 设置为默认排序字段、快速搜索字段
                        state.table.quickSearchField.push(data.uuid!)
                        if (!state.table.defaultSortField) {
                            state.table.defaultSortField = data.uuid!
                        }
                    } else {
                        return evt.item.remove()
                    }
                }

                // 出现权重字段则以其排序
                if (data.designType == 'weigh') {
                    state.table.defaultSortField = data.uuid!
                }

                // name 重复时，自动重命名
                data.name = autoRenameRepeatField(data.name)

                // 插入字段
                state.fields.splice(evt.newIndex!, 0, data)

                logTableDesignChange({
                    type: 'add-field',
                    index: evt.newIndex!,
                    newName: data.name,
                    oldName: '',
                    after: evt.newIndex === 0 ? 'FIRST FIELD' : state.fields[evt.newIndex! - 1].name,
                })

                // 远程下拉参数预填
                if (['remoteSelect', 'remoteSelects'].includes(data.designType)) {
                    showRemoteSelectPre(evt.newIndex!, true)
                }

                // 表单表格字段预定义
                if (!data.formBuildExclude) {
                    state.table.formFields.push(data.uuid!)
                }
                if (!data.tableBuildExclude) {
                    state.table.columnFields.push(data.uuid!)
                }
            }
            evt.item.remove()
            nextTick(() => {
                sortable.sort(range(state.fields.length).map((value) => value.toString()))
            })
        },
        onEnd: (evt) => {
            const temp = state.fields[evt.oldIndex!]
            state.fields.splice(evt.oldIndex!, 1)
            state.fields.splice(evt.newIndex!, 0, temp)

            logTableDesignChange({
                type: 'change-field-order',
                index: evt.newIndex!,
                newName: '',
                oldName: temp.name,
                after: evt.newIndex === 0 ? 'FIRST FIELD' : state.fields[evt.newIndex! - 1].name,
            })

            nextTick(() => {
                sortable.sort(range(state.fields.length).map((value) => value.toString()))
            })
        },
    })

    tabsRefs.value.forEach((item, index) => {
        Sortable.create(item, {
            sort: false,
            group: {
                name: 'design-field',
                pull: 'clone',
                put: false,
            },
            animation: 200,
            setData: (dataTransfer) => {
                dataTransfer.setData('name', Object.keys(fieldItem)[index])
            },
            onStart: () => {
                state.draggingField = true
            },
            onEnd: () => {
                state.draggingField = false
            },
        })
    })
})

/**
 * 修改表名（失焦时校验，路径填充由 watch 统一触发避免重复请求）
 */
const onTableNameChange = (val: string) => {
    if (!val) return (state.error.tableName = '')
    if (/^[a-z_][a-z0-9_]*$/.test(val)) {
        state.error.tableName = ''
    } else {
        state.error.tableName = t('crud.crud.Use lower case underlined for table names')
    }
    tableDesignChangeInit()
}

/** 相对路径输入时防抖触发路径填充 */
const debouncedOnRelativePathInput = debounce((val: string) => {
    if (val) onTableChange(val)
}, 400)

/** 监听表名变化，统一防抖触发路径填充（唯一入口，避免 watch + onInput 重复请求） */
watch(
    () => state.table.name,
    debounce((val: string) => {
        if (val && /^[a-z_][a-z0-9_]*$/.test(val)) {
            onTableChange(val)
        }
    }, 400)
)

const tableDesignChangeInit = () => {
    state.table.rebuild = 'No'
    state.table.designChange = []
}

/**
 * 预获取一个表的生成数据
 * @param val 新表名
 */
const onTableChange = (val: string) => {
    if (!val) return
    getFileData(val, state.table.isCommonModel)
        .then((res) => {
            state.table.modelFile = res.data.modelFile
            state.table.controllerFile = res.data.controllerFile
            state.table.validateFile = res.data.validateFile
            state.table.webViewsDir = res.data.webViewsDir
            state.table.generateRelativePath = val.replaceAll('/', '\\')
        })
        .catch(() => {
            // 接口失败时静默处理，避免重复弹窗（axios 已统一处理错误提示）
        })
}

const onChangeCommonModel = () => {
    const table = state.table.name || state.table.generateRelativePath?.replace(/\\/g, '/')
    if (table) onTableChange(table)
}

const onJoinTableChange = () => {
    if (!state.remoteSelectPre.form.table) return

    // 重置远程下拉信息表单
    resetRemoteSelectForm(['table'])

    state.loading.remoteSelect = true
    getTableFieldList(state.remoteSelectPre.form.table, true, state.table.databaseConnection)
        .then((res) => {
            state.remoteSelectPre.form.pk = res.data.pk

            const preLabel = ['name', 'title', 'username', 'nickname']
            for (const key in res.data.fieldList) {
                if (preLabel.includes(key)) {
                    state.remoteSelectPre.form.label = key
                    state.remoteSelectPre.form.joinField.push(key)
                    break
                }
            }

            const fieldSelect: anyObj = {}
            for (const key in res.data.fieldList) {
                fieldSelect[key] = (key ? key + ' - ' : '') + res.data.fieldList[key]
            }
            state.remoteSelectPre.fieldList = fieldSelect
        })
        .finally(() => {
            state.loading.remoteSelect = false
        })

    getFileData(state.remoteSelectPre.form.table).then((res) => {
        state.remoteSelectPre.modelFileList = res.data.modelFileList
        state.remoteSelectPre.controllerFileList = res.data.controllerFileList

        if (Object.keys(res.data.modelFileList).includes(res.data.modelFile)) {
            state.remoteSelectPre.form.modelFile = res.data.modelFile
        }
        if (Object.keys(res.data.controllerFileList).includes(res.data.controllerFile)) {
            state.remoteSelectPre.form.controllerFile = res.data.controllerFile
        }
    })
}

const onSaveRemoteSelect = () => {
    const submitCallback = () => {
        // 修改字段名
        if (state.fields[state.remoteSelectPre.index].name == 'remote_select') {
            const newName =
                state.remoteSelectPre.form.table + (state.fields[state.remoteSelectPre.index].designType == 'remoteSelect' ? '_id' : '_ids')
            onFieldNameChange(newName, state.remoteSelectPre.index)
        }

        state.fields[state.remoteSelectPre.index].form['remote-table'].value = state.remoteSelectPre.form.table
        state.fields[state.remoteSelectPre.index].form['remote-pk'].value = state.remoteSelectPre.form.pk
        state.fields[state.remoteSelectPre.index].form['remote-field'].value = state.remoteSelectPre.form.label
        state.fields[state.remoteSelectPre.index].form['remote-controller'].value = state.remoteSelectPre.form.controllerFile
        state.fields[state.remoteSelectPre.index].form['remote-model'].value = state.remoteSelectPre.form.modelFile
        state.fields[state.remoteSelectPre.index].form['remote-url'].value = state.remoteSelectPre.form.remoteUrl
        state.fields[state.remoteSelectPre.index].form['remote-source-config-type'].value = state.remoteSelectPre.form.sourceConfigType
        state.fields[state.remoteSelectPre.index].form['remote-primary-table-alias'].value = state.remoteSelectPre.form.primaryTableAlias

        state.fields[state.remoteSelectPre.index].form['relation-fields'].value =
            state.fields[state.remoteSelectPre.index].designType == 'remoteSelect'
                ? state.remoteSelectPre.form.joinField.join(',')
                : state.remoteSelectPre.form.label

        state.remoteSelectPre.index = -1
        state.remoteSelectPre.show = false
        resetRemoteSelectForm()
    }

    if (formRef.value) {
        formRef.value.validate((valid) => {
            if (valid) {
                submitCallback()
            }
        })
    }
}

const onCancelRemoteSelect = () => {
    state.remoteSelectPre.show = false
    resetRemoteSelectForm()
    if (state.remoteSelectPre.index !== -1 && state.remoteSelectPre.hideDelField) {
        onDelField(state.remoteSelectPre.index)
    }
}

const resetRemoteSelectForm = (excludes: string[] = []) => {
    for (const key in state.remoteSelectPre.form) {
        if (excludes.includes(key)) continue
        if (key == 'joinField') {
            state.remoteSelectPre.form[key] = []
        } else if (key == 'sourceConfigType') {
            state.remoteSelectPre.form[key] = 'crud'
        } else {
            ;(state.remoteSelectPre.form[key as keyof typeof state.remoteSelectPre.form] as string) = ''
        }
    }
}

const remoteSelectPreFormRules: Partial<Record<string, FormItemRule[]>> = reactive({
    table: [buildValidatorData({ name: 'required', title: t('crud.crud.remote-table') })],
    pk: [buildValidatorData({ name: 'required', title: t('crud.crud.Drop down value field') })],
    label: [buildValidatorData({ name: 'required', title: t('crud.crud.Drop down label field') })],
    joinField: [buildValidatorData({ name: 'required', title: t('crud.crud.Fields displayed in the table') })],
    controllerFile: [buildValidatorData({ name: 'required', title: t('crud.crud.Controller position') })],
    modelFile: [buildValidatorData({ name: 'required', title: t('crud.crud.Data Model Location') })],
    remoteUrl: [buildValidatorData({ name: 'required', title: t('crud.crud.remote-url') })],
})

const logTableDesignChange = (data: TableDesignChange) => {
    if (crudState.type == 'create') return
    let push = true
    if (data.type == 'change-field-name') {
        for (const key in state.table.designChange) {
            // 有属性修改记录的字段被改名-单独循环防止字段再次改名后造成找不到属性修改记录
            if (state.table.designChange[key].type == 'change-field-attr' && data.oldName == state.table.designChange[key].oldName) {
                state.table.designChange[key].oldName = data.newName
            }
            // 有排序记录的字段被改名
            if (state.table.designChange[key].type == 'change-field-order' && data.oldName == state.table.designChange[key].oldName) {
                state.table.designChange[key].oldName = data.newName
            }
            if (state.table.designChange[key].after == data.oldName) {
                state.table.designChange[key].after = data.newName
            }
        }
        for (const key in state.table.designChange) {
            // 新增字段改名
            if (state.table.designChange[key].type == 'add-field' && state.table.designChange[key].newName == data.oldName) {
                state.table.designChange[key].newName = data.newName
                push = false
                // 同一字段不会有两条新增记录
                break
            }
            // 字段再次改名
            if (state.table.designChange[key].type == 'change-field-name' && state.table.designChange[key].newName == data.oldName) {
                data.oldName = state.table.designChange[key].oldName
                state.table.designChange[key] = data

                // 取消字段改名
                if (state.table.designChange[key].newName == state.table.designChange[key].oldName) {
                    state.table.designChange.splice(key as any, 1)
                }

                push = false
                break
            }
        }
    } else if (data.type == 'del-field') {
        let add = false
        state.table.designChange = state.table.designChange.filter((item) => {
            // 新增的字段被删除
            add = item.type == 'add-field' && item.newName == data.oldName
            // 有属性修改记录的字段被删除
            const attr = item.type == 'change-field-attr' && item.oldName == data.oldName
            // 有排序记录的字段被删除
            const order = item.type == 'change-field-order' && item.oldName == data.oldName

            return !add && !attr && !order
        })

        // 有改名记录的字段被删除（延后单独处理避免先改名再改属性的情况）
        state.table.designChange = state.table.designChange.filter((item) => {
            const name = item.type == 'change-field-name' && item.newName == data.oldName
            if (name) data.oldName = item.oldName
            return !name
        })

        // 添加的字段需要过滤掉记录同时不记录删除操作
        if (add) push = false

        for (const key in state.table.designChange) {
            // 同一字段名称多次删除（删除后添加再删除）
            if (state.table.designChange[key].type == 'del-field' && state.table.designChange[key].oldName == data.oldName) {
                push = false
                break
            }
        }
    } else if (data.type == 'change-field-attr') {
        // 先改名再改属性无需处理
        for (const key in state.table.designChange) {
            // 重复修改属性只记录一次
            if (state.table.designChange[key].type == 'change-field-attr' && state.table.designChange[key].oldName == data.oldName) {
                push = false
                break
            }
            // 新增的字段无需记录属性修改
            if (state.table.designChange[key].type == 'add-field' && state.table.designChange[key].newName == data.oldName) {
                push = false
                break
            }
        }
    } else if (data.type == 'change-field-order') {
        for (const key in state.table.designChange) {
            // 新增的字段
            if (state.table.designChange[key].type == 'add-field' && state.table.designChange[key].newName == data.oldName) {
                // 更新排序设定
                state.table.designChange[key].after = data.after
                push = false
                break
            }
            // 重复的排序记录
            if (state.table.designChange[key].type == 'change-field-order' && state.table.designChange[key].oldName == data.oldName) {
                state.table.designChange[key] = data
                push = false
                break
            }
        }
    }
    data.sync = true
    if (push) state.table.designChange.push(data)
}

const getTableDesignChangeContent = (data: TableDesignChange): string => {
    switch (data.type) {
        case 'add-field':
            return t('crud.crud.Add field') + ' ' + data.newName
        case 'change-field-attr':
            return t('crud.crud.Modify field properties') + ' ' + data.oldName
        case 'change-field-name':
            return t('crud.crud.Modify field name') + ' ' + data.oldName + ' => ' + data.newName
        case 'del-field':
            return t('crud.crud.Delete field') + ' ' + data.oldName
        case 'change-field-order':
            return (
                t('crud.crud.Modify field order') +
                ' ' +
                data.oldName +
                ' => ' +
                (data.after == 'FIRST FIELD' ? t('crud.crud.First field') : data.after + ' ' + t('crud.crud.After'))
            )
        default:
            return t('Unknown')
    }
}

const getTableDesignTimelineType = (type: TableDesignChangeType): TimelineItemProps['type'] => {
    let timeline = ''
    switch (type) {
        case 'change-field-name':
            timeline = 'warning'
            break
        case 'del-field':
            timeline = 'danger'
            break
        case 'add-field':
            timeline = 'primary'
            break
        case 'change-field-attr':
            timeline = 'success'
            break
        case 'change-field-order':
            timeline = 'info'
            break
        default:
            timeline = 'success'
            break
    }
    return timeline as TimelineItemProps['type']
}
</script>

<style scoped lang="scss">
.form-item-position-right {
    display: flex !important;
    align-items: center;
    :deep(.el-form-item__label) {
        margin-bottom: 0 !important;
    }
}
.default-main {
    margin-bottom: 0;
}
.mt-10 {
    margin-top: 10px;
}
.mr-20 {
    margin-right: 20px;
}
.field-collapse :deep(.el-collapse-item__header) {
    padding-left: 10px;
    user-select: none;
}
.field-box {
    padding: 10px;
}
.field-item {
    display: inline-block;
    padding: 3px 16px;
    border: 1px dashed var(--el-border-color);
    border-radius: var(--el-border-radius-base);
    margin: 6px;
    cursor: pointer;
    user-select: none;
    &:hover {
        border-color: var(--el-color-primary);
    }
}
.header-config-box {
    position: relative;
    .header-senior-config {
        display: flex;
        align-items: center;
        justify-content: center;
        position: absolute;
        height: 24px;
        bottom: -24px;
        padding: 4px 20px;
        padding-top: 0;
        left: calc(50% - 10px);
        font-size: var(--el-font-size-small);
        border-bottom-left-radius: 50px;
        border-bottom-right-radius: 50px;
        background-color: var(--ba-bg-color-overlay);
        color: var(--el-text-color-primary);
        cursor: pointer;
        user-select: none;
        .senior-config-arrow-icon {
            margin-left: 4px;
        }
    }
}
.header-senior-config-box {
    width: 100%;
    padding: 10px;
    background-color: var(--ba-bg-color-overlay);
}
.header-senior-config-form {
    width: 50%;
    :deep(.el-form-item__label) {
        justify-content: flex-start;
    }
}
.header-box {
    display: flex;
    align-items: center;
    height: v-bind("state.error.tableName ? '70px':'60px'");
    padding: 10px;
    background-color: var(--ba-bg-color-overlay);
    border-radius: var(--el-border-radius-base);
    transition: 0.1s;
    .header,
    .header-item-box {
        display: flex;
        width: 100%;
        align-items: center;
        justify-content: center;
        white-space: nowrap;
        :deep(.el-form-item) {
            margin-bottom: 0;
        }
    }
    .header-item-box {
        width: 50%;
    }
    .table-name-item {
        flex: 3;
    }
    .table-comment-item {
        flex: 4;
    }
    .header-right {
        margin-left: auto;
        .design-change-log {
            margin-right: 10px;
        }
    }
}
.default-sort-field-box {
    display: flex;
    .default-sort-field {
        flex: 6;
    }
    .default-sort-field-type {
        flex: 3;
    }
}
.fields-box {
    margin-top: 36px;
}
.design-field-empty {
    display: flex;
    height: 100%;
    color: var(--el-color-info);
    font-size: var(--el-font-size-medium);
    align-items: center;
    justify-content: center;
}
.design-window {
    overflow-x: auto;
    height: calc(100vh - 200px);
    border-radius: var(--el-border-radius-base);
    background-color: var(--ba-bg-color-overlay);
    border: v-bind('state.draggingField ? "1px dashed var(--el-color-primary)":(state.fields.length ? "none":"1px dashed var(--el-border-color)")');
    .design-field-box {
        display: flex;
        padding: 10px;
        align-items: center;
        border: 1px dashed var(--el-border-color);
        border-radius: var(--el-border-radius-base);
        margin-bottom: 2px;
        cursor: pointer;
        user-select: none;
        .design-field {
            padding-right: 10px;
        }
        .design-field-name-input {
            width: 200px;
        }
        .design-field-name-comment {
            width: 100px;
        }
        .design-field-right {
            margin-left: auto;
        }
        &:hover {
            border-color: var(--el-color-primary);
        }
    }
    .design-field-box.activate {
        border-color: var(--el-color-primary);
    }
}
.field-inline {
    display: flex;
    :deep(.el-form-item) {
        width: 46%;
        margin-right: 2%;
    }
}
.default-input {
    margin-top: 10px;
}
.field-config {
    overflow-x: auto;
    height: calc(100vh - 200px);
    padding: 20px;
    background-color: var(--ba-bg-color-overlay);
}
:deep(.confirm-generate-dialog) .el-dialog__body {
    height: unset;
}
.confirm-generate-dialog-body {
    padding: 30px;
}
.confirm-generate-dialog-footer {
    display: flex;
    align-items: center;
    justify-content: center;
}
:deep(.design-change-log-dialog) .el-dialog__body {
    height: unset;
    padding-top: 20px;
    .design-change-log-timeline {
        padding-left: 10px;
        .el-timeline-item .el-timeline-item__node {
            top: 3px;
        }
    }
    .design-change-tips {
        display: block;
        margin-bottom: 20px;
        color: var(--el-color-info);
        font-size: var(--el-font-size-small);
    }
    .rebuild-form-item {
        padding-top: 20px;
        border-top: 1px solid var(--el-border-color-lighter);
    }
}
</style>
