<template>
  <el-dialog
    :visible.sync="dialogVisible"
    :title="dialogTitle"
    :fullscreen="true"
    append-to-body
    custom-class="article-editor-dialog"
    :close-on-click-modal="false"
    @open="handleOpen"
  >
    <div class="article-editor">
      <div class="article-editor__left">
        <div class="article-editor__form">
          <el-form :model="form" label-width="76px" size="small">
            <el-form-item label="文章标题">
              <el-input v-model.trim="form.title" placeholder="请输入文章标题" />
            </el-form-item>
            <el-form-item label="文章摘要">
              <el-input
                v-model.trim="form.summary"
                type="textarea"
                :rows="2"
                maxlength="120"
                show-word-limit
                placeholder="请输入文章摘要"
              />
            </el-form-item>
            <div class="article-editor__inline">
              <el-form-item label="文章类型">
                <el-select v-model="form.type" placeholder="请选择文章类型">
                  <el-option label="企业动态" value="企业动态" />
                  <el-option label="产品动态" value="产品动态" />
                  <el-option label="行业洞察" value="行业洞察" />
                  <el-option label="活动资讯" value="活动资讯" />
                </el-select>
              </el-form-item>
              <el-form-item label="发布时间">
                <el-date-picker
                  v-model="form.publishTime"
                  type="date"
                  value-format="yyyy-MM-dd"
                  placeholder="选择发布时间"
                />
              </el-form-item>
            </div>
            <el-form-item label="封面配图">
              <div class="cover-row">
                <el-button type="primary" size="small" @click="$refs.coverInput.click()">上传图片</el-button>
                <el-input
                  v-model.trim="form.coverUrl"
                  class="cover-url-input"
                  placeholder="可填写图片地址，或点击左侧上传本地图片"
                  @input="handleCoverUrlInput"
                />
                <span v-if="form.coverName" class="cover-name">{{ form.coverName }}</span>
                <input ref="coverInput" type="file" accept="image/*" class="hidden-input" @change="handleCoverUpload">
              </div>
            </el-form-item>
          </el-form>
        </div>


        <div v-if="dialogVisible" class="wang-editor-wrap">
          <Toolbar
            class="wang-toolbar"
            :editor="editor"
            :default-config="toolbarConfig"
            :mode="editorMode"
          />
          <Editor
            v-model="form.content"
            class="wang-editor"
            :default-config="editorConfig"
            :mode="editorMode"
            @onCreated="handleEditorCreated"
          />
        </div>
      </div>

      <div
        class="resize-handle"
        title="拖动调整预览宽度"
        @mousedown="startResize"
      ></div>

      <div class="article-editor__preview" :style="{ width: `${previewWidth}px` }">
        <div class="preview-head">
          <span>实时预览</span>
          <em>自动同步</em>
        </div>
        <div class="preview-card">
          <div v-if="coverImage" class="preview-cover">
            <img :src="coverImage" alt="cover">
          </div>
          <span class="preview-type">{{ form.type || '企业动态' }}</span>
          <h1>{{ form.title || '文章标题' }}</h1>
          <p class="preview-summary">{{ form.summary || '文章摘要预览...' }}</p>
          <p class="preview-date">{{ form.publishTime || today }}</p>
          <div class="preview-content" v-html="form.content || '文章内容预览...'"></div>
        </div>
      </div>
    </div>

    <div slot="footer" class="article-editor-footer">
      <span class="editor-status">{{ dirty ? '未保存' : '已同步' }}</span>
      <div>
        <el-button size="small" @click="dialogVisible = false">取消</el-button>
        <el-button size="small" @click="handleSave('draft')">保存草稿</el-button>
        <el-button type="primary" size="small" @click="handleSave('published')">发布文章</el-button>
      </div>
    </div>
  </el-dialog>
</template>

<script>
import '@wangeditor/editor/dist/css/style.css'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'

const normalizeImageUrl = (url) => {
  if (!url) return ''
  if (/^(https?:|blob:|data:|\/\/)/.test(url)) return url
  return `${window.location.origin}/idfile?path=${url}`
}

export default {
  name: 'ArticleEditorDialog',
  components: { Editor, Toolbar },
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    article: {
      type: Object,
      default: null
    }
  },
  data() {
    return {
      // 标记当前表单是否有未保存改动。
      dirty: false,
      // 保存 wangEditor 实例，组件销毁时需要手动释放。
      editor: null,
      // wangEditor 模式配置。
      editorMode: 'default',
      // wangEditor 工具栏配置。
      toolbarConfig: {},
      // wangEditor 编辑器配置。
      editorConfig: {
        placeholder: '请输入文章内容...',
        MENU_CONF: {
          uploadImage: {
            customUpload: (file, insertFn) => {
              // 正文图片参考商品上传做法：先保存 File，提交时随文章 FormData 一起传。
              this.form.contentFiles.push(file)
              // 本地生成预览地址，插入到编辑器内容中。
              const url = URL.createObjectURL(file)
              insertFn(url, file.name, url)
            }
          }
        }
      },
      // 上传封面的本地预览地址，只用于页面显示，不提交给接口。
      coverPreview: '',
      // 右侧预览区域宽度。
      previewWidth: 400,
      // 标记预览区域是否正在拖拽调整宽度。
      resizing: false,
      // 记录拖拽开始时的鼠标位置。
      resizeStartX: 0,
      // 记录拖拽开始时的预览宽度。
      resizeStartWidth: 400,
      // 文章编辑表单数据。
      form: this.createEmptyForm()
    }
  },
  computed: {
    dialogVisible: {
      get() {
        // 使用父组件传入的 visible 控制弹窗显示。
        return this.visible
      },
      set(value) {
        // 通过 sync 事件通知父组件更新 visible。
        this.$emit('update:visible', value)
      }
    },
    dialogTitle() {
      // 有文章数据时是编辑，否则是新建。
      return this.article ? '编辑文章' : '新建文章'
    },
    today() {
      // 生成默认发布时间，格式与接口要求一致。
      const date = new Date()
      const month = `${date.getMonth() + 1}`.padStart(2, '0')
      const day = `${date.getDate()}`.padStart(2, '0')
      return `${date.getFullYear()}-${month}-${day}`
    },
    coverImage() {
      // 优先使用本地预览，没有预览时使用接口/手动输入的图片路径。
      if (this.coverPreview) return this.coverPreview
      return normalizeImageUrl(this.form.coverUrl)
    }
  },
  watch: {
    form: {
      deep: true,
      handler() {
        // 任意表单字段变化后标记为未保存。
        this.dirty = true
      }
    }
  },
  beforeDestroy() {
    // 组件销毁前释放 wangEditor 实例，避免内存泄漏。
    if (this.editor) {
      this.editor.destroy()
      this.editor = null
    }
    // 清理拖拽监听事件。
    this.removeResizeListeners()
  },
  methods: {
    createEmptyForm() {
      // 创建新增文章时使用的默认表单结构。
      return {
        id: '',
        title: '',
        summary: '',
        type: '企业动态',
        status: '0',
        publishTime: '',
        views: 0,
        content: '',
        contentFiles: [],
        coverUrl: '',
        coverFile: null,
        coverName: ''
      }
    },
    handleOpen() {
      // 弹窗打开时合并默认值和待编辑文章数据。
      this.form = {
        ...this.createEmptyForm(),
        ...(this.article || {})
      }
      // 没有发布时间时默认使用当天日期。
      if (!this.form.publishTime) this.form.publishTime = this.today
      // 初始化封面预览，新上传前优先显示接口返回的封面地址。
      this.coverPreview = ''
      this.$nextTick(() => {
        // 表单初始化完成后重置未保存状态。
        this.dirty = false
      })
    },
    handleEditorCreated(editor) {
      // 保存编辑器实例，后续销毁时使用。
      this.editor = Object.seal(editor)
    },
    handleCoverUpload(event) {
      // 读取用户选择的封面文件。
      const file = event.target.files && event.target.files[0]
      if (!file) return
      // 保存文件名用于页面提示。
      this.form.coverName = file.name
      // 保存原始文件，父组件提交时会放入 FormData 的 cover_img。
      this.form.coverFile = file
      // 生成本地预览地址，注意这个地址只用于预览，不提交给接口。
      this.coverPreview = URL.createObjectURL(file)
      // 清空 input 值，保证重复选择同一文件也能触发 change。
      event.target.value = ''
    },
    handleCoverUrlInput() {
      // 用户手动输入图片路径时，清空本地上传预览状态。
      this.coverPreview = ''
      this.form.coverFile = null
      this.form.coverName = ''
    },
    startResize(event) {
      // 开始拖拽调整右侧预览区域宽度。
      this.resizing = true
      // 记录拖拽起点。
      this.resizeStartX = event.clientX
      // 记录拖拽开始时的宽度。
      this.resizeStartWidth = this.previewWidth
      // 修改全局鼠标样式，让拖拽反馈更明显。
      document.body.style.cursor = 'col-resize'
      // 禁止选中文本，避免拖拽时误选页面文字。
      document.body.style.userSelect = 'none'
      // 监听鼠标移动和松开事件。
      window.addEventListener('mousemove', this.handleResize)
      window.addEventListener('mouseup', this.stopResize)
    },
    handleResize(event) {
      // 非拖拽状态下不处理移动事件。
      if (!this.resizing) return
      // 根据鼠标偏移计算新的预览宽度。
      const delta = this.resizeStartX - event.clientX
      const nextWidth = this.resizeStartWidth + delta
      // 限制预览宽度范围，避免过窄或过宽。
      this.previewWidth = Math.min(Math.max(nextWidth, 320), 720)
    },
    stopResize() {
      // 结束拖拽状态。
      this.resizing = false
      // 恢复全局鼠标样式。
      document.body.style.cursor = ''
      // 恢复页面文本选择。
      document.body.style.userSelect = ''
      // 移除拖拽事件监听。
      this.removeResizeListeners()
    },
    removeResizeListeners() {
      // 移除鼠标移动监听。
      window.removeEventListener('mousemove', this.handleResize)
      // 移除鼠标松开监听。
      window.removeEventListener('mouseup', this.stopResize)
    },
    handleSave(status) {
      // 第一步：校验文章标题。
      if (!this.form.title) {
        this.$message.warning('请输入文章标题')
        return
      }
      // 第二步：校验文章摘要。
      if (!this.form.summary) {
        this.$message.warning('请输入文章摘要')
        return
      }
      // 第三步：校验发布时间，确保提交接口时有 publish_time。
      if (!this.form.publishTime) {
        this.$message.warning('请选择发布时间')
        return
      }
      // 第四步：校验封面图片，确保提交接口时有 cover_img。
      if (!this.form.coverFile && !this.form.coverUrl) {
        this.$message.warning('请上传封面图片或填写图片地址')
        return
      }
      // 第五步：校验文章正文。
      if (!this.form.content) {
        this.$message.warning('请输入文章正文')
        return
      }
      // 第六步：组装保存参数，status 由按钮决定是草稿还是发布。
      const payload = {
        ...this.form,
        publish_time: this.form.publishTime,
        status
      }
      // 第七步：把保存参数交给父组件，由父组件调用新增或编辑接口。
      this.$emit('save', payload)
      // 第八步：保存触发后重置未保存状态。
      this.dirty = false
      // 第九步：关闭弹窗。
      this.dialogVisible = false
    }
  }
}
</script>

<style scoped>
::v-deep .article-editor-dialog {
  margin: 0 !important;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

::v-deep .article-editor-dialog .el-dialog__header {
  flex-shrink: 0;
  padding: 16px 24px;
  border-bottom: 1px solid #edf0f5;
}

::v-deep .article-editor-dialog .el-dialog__body {
  flex: 1;
  min-height: 0;
  padding: 0 24px;
  overflow: hidden;
}

::v-deep .article-editor-dialog .el-dialog__footer {
  flex-shrink: 0;
  padding: 14px 24px;
  border-top: 1px solid #edf0f5;
}

.article-editor {
  height: 100%;
  display: flex;
  overflow: hidden;
}

.article-editor__left {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.resize-handle {
  position: relative;
  flex: 0 0 10px;
  width: 10px;
  cursor: col-resize;
  background: #f8fafc;
  border-left: 1px solid #edf0f5;
  border-right: 1px solid #edf0f5;
  transition: background 0.2s ease;
}

.resize-handle::before {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 3px;
  height: 42px;
  content: '';
  border-radius: 999px;
  background: #cbd5e1;
  transform: translate(-50%, -50%);
  transition: background 0.2s ease, height 0.2s ease;
}

.resize-handle:hover {
  background: #eff6ff;
}

.resize-handle:hover::before {
  height: 58px;
  background: #3b82f6;
}

.article-editor__form {
  padding: 16px 18px 4px 0;
  border-bottom: 1px solid #edf0f5;
}

.article-editor__inline {
  display: flex;
  gap: 16px;
}

.article-editor__inline .el-form-item {
  flex: 1;
}

.cover-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.cover-url-input {
  flex: 1;
}

.cover-name {
  color: #16a34a;
  font-size: 13px;
}

.hidden-input {
  display: none;
}

.article-editor__toolbar-tip {
  padding: 10px 18px 10px 0;
  color: #8a94a6;
  font-size: 12px;
}

.wang-editor-wrap {
  flex: 1;
  min-height: 0;
  padding-right: 18px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.wang-toolbar {
  border: 1px solid #edf0f5;
  border-bottom: 0;
  border-radius: 10px 10px 0 0;
}

.wang-editor {
  flex: 1;
  min-height: 0;
  border: 1px solid #edf0f5;
  border-radius: 0 0 10px 10px;
  overflow-y: auto;
}

.article-editor__preview {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  background: #f8fafc;
}

.preview-head {
  height: 48px;
  padding: 0 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid #edf0f5;
}

.preview-head span {
  color: #344054;
  font-size: 14px;
  font-weight: 600;
}

.preview-head em {
  color: #9ca3af;
  font-size: 12px;
  font-style: normal;
}

.preview-card {
  flex: 1;
  min-height: 0;
  margin: 18px;
  padding: 24px;
  overflow-y: auto;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 8px 22px rgba(15, 23, 42, 0.06);
}

.preview-cover {
  margin-bottom: 16px;
}

.preview-cover img {
  width: 100%;
  border-radius: 10px;
}

.preview-type {
  display: inline-flex;
  padding: 2px 8px;
  margin-bottom: 10px;
  color: #2563eb;
  font-size: 12px;
  background: #eff6ff;
  border-radius: 999px;
}

.preview-card h1 {
  margin: 0 0 8px;
  color: #1f2937;
  font-size: 30px;
  font-weight: 700;
  line-height: 1.4;
}

.preview-date {
  margin: 0 0 18px;
  color: #9ca3af;
  font-size: 12px;
}

.preview-summary {
  margin: 0 0 10px;
  color: #667085;
  font-size: 14px;
  line-height: 1.7;
}

.preview-content {
  color: #4b5563;
  font-size: 14px;
  line-height: 1.8;
}

.article-editor-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.editor-status {
  color: #9ca3af;
  font-size: 12px;
}
</style>
