<!--
 * @Author: wanglj wanglijie@dashoo.cn
 * @Date: 2025-03-11 18:02:10
 * @LastEditors: wanglj wanglijie@dashoo.cn
 * @LastEditTime: 2025-03-19 18:23:43
 * @FilePath: \vant-demo-master\vant\vue3-ts\src\view\login\index.vue
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
  <div class="register">
    <div class="form">
      <van-row class="pl10 pr10 pt10">
        <van-button type="success" @click="changeType('10')">
          注册课题组成员
        </van-button>
        <van-button type="primary" @click="changeType('20')">
          注册课题组负责人
        </van-button>
      </van-row>
      <van-steps :active="state.active" class="pl20 pr20 pt10">
        <van-step>登录信息</van-step>
        <van-step>个人信息</van-step>
        <van-step v-if="state.form.registerType == '20'">项目信息</van-step>
      </van-steps>
      <van-form ref="loginInfoRef" v-show="state.active == 0" required="auto">
        <van-cell-group inset>
          <van-field v-model="state.form.userName" label="登录账号" placeholder="登录账号" :rules="[{ required: true, message: '请填写登录账号' },
            {validator: checkUserNameExists, message: '账号不可用'}]" />
          <van-field v-model="state.form.password" :type="state.showPassword ? 'text' : 'password'" label="密码" placeholder="密码"
            :right-icon="state.showPassword ? 'eye-o' : 'closed-eye'"
            @click-right-icon="state.showPassword = !state.showPassword"
            :rules="[{ required: true, message: '密码不能为空'}, {validator: checkPassword, message: '密码不合法'}]" />
          <van-field v-model="state.form.confirmPassword" :type="state.showConfirmPassword ? 'text' : 'password'" label="确认密码" placeholder="确认密码"
            :right-icon="state.showConfirmPassword ? 'eye-o' : 'closed-eye'"
            @click-right-icon="state.showConfirmPassword = !state.showConfirmPassword"
            :rules="[{ required: true, validator: confirmPasswordSame, message: '两次输入的密码不一致' }]" />
        </van-cell-group>
      </van-form>
      <van-form ref="personInfoRef" v-show="state.active == 1" required="auto">
        <van-cell-group inset>
          <van-field v-model="state.form.nickName" label="姓名" placeholder="姓名"
            :rules="[{ required: true, message: '请填写姓名' }]" />
          <van-field label="性别" :rules="[{ required: false }]">
            <template #input>
              <van-radio-group v-model="state.form.sex" label="性别" placeholder="性别" direction="horizontal">
                <van-radio v-for="item in userSexList" :name="item.dictValue" :key="item.dictValue">
                  {{ item.dictLabel }}
                </van-radio>
              </van-radio-group>
            </template>
          </van-field>
          <van-field>
            <template #label>
              <span>用户类型</span>
              <el-tooltip class="box-item" effect="dark" placement="top" append-to="body">
                <template #content>
                  <div v-for="item in UserTypeTooltip" :key="item">
                    {{ item }}
                  </div>
                </template>
                <el-icon style="margin-left: 10px;">
                  <QuestionFilled />
                </el-icon>
              </el-tooltip>
            </template>
            <template #input>
              <van-radio-group v-model="state.form.userType" label="用户类型" placeholder="用户类型" direction="horizontal">
                <van-radio v-for="item in userTypeList" :name="item.dictValue" :key="item.dictValue">
                  {{ item.dictLabel }}
                </van-radio>
              </van-radio-group>
            </template>
          </van-field>
          <van-field v-model="state.form.deptName" is-link readonly label="组织部门" placeholder="组织部门"
            @click="showDeptPickerHandler" :rules="[{ required: true, message: '请选择组织部门' }]" />
          <van-field v-if="needInputDeptInfo" v-model="state.form.deptDescribe" label="部门描述" placeholder="部门描述"
            :rules="[{ required: true, message: '请填写部门描述' }]" />
          <van-field v-model="state.form.phone" type="tel" label="手机号" placeholder="手机号"
            @blur="checkUserNamePhoneExists('phone')" :rules="[{ required: true, message: '请填写手机号' }]" />
          <van-field v-model="state.form.email" label="邮箱" placeholder="邮箱"
            :rules="[{ required: true, message: '请填写邮箱' }]" />
          <van-field label="证件类型" :rules="[{ required: true, message: '请选择证件类型' }]">
            <template #input>
              <van-radio-group disabled v-model="state.form.idType" label="证件类型" placeholder="证件类型"
                direction="horizontal">
                <van-radio v-for="item in userCertList" :name="item.dictValue" :key="item.id">
                  {{ item.dictLabel }}
                </van-radio>
              </van-radio-group>
            </template>
          </van-field>
          <van-field v-model="state.form.idCode" label="证件号" placeholder="证件号"
            :rules="[{ required: true, message: '请填写证件号' }]" />
          <van-field v-model="state.form.projectGroupName" v-if="state.form.registerType === '20'" label="课题组"
            placeholder="课题组" :rules="[{ required: true, message: '请填写课题组' }]" />
          <template v-else>
            <van-field :disabled="!state.form.deptId" v-model="state.form.projectGroupName" is-link readonly label="课题组"
              placeholder="课题组" :rules="[{ required: true, message: '请填写课题组' }]" @click="showPjtPicker = true" />
          </template>
        </van-cell-group>
      </van-form>
      <van-form ref="projectInfoRef" v-show="state.active == 2" required="auto">
        <van-cell-group inset>
          <div class="project-list-container">
            <van-button type="primary" block @click="addProject" class="add-project-btn">
              新增项目
            </van-button>
            <div v-if="state.form.projectList.length === 0" class="empty-project-tip">
              请至少添加一个项目
            </div>
            <div v-for="(item, index) in state.form.projectList" :key="index" class="project-card">
              <div class="project-card-header">
                <span class="project-card-title">项目 {{ index + 1 }}</span>
                <van-button 
                  v-if="state.form.projectList.length > 1"
                  size="mini" 
                  type="danger" 
                  plain
                  @click="delProjectItem(index)"
                  class="delete-btn">
                  删除
                </van-button>
              </div>
              <div class="project-card-body">
                <van-field 
                  v-model="item.projectName" 
                  label="项目名称" 
                  placeholder="请输入项目名称"
                  :rules="[{ required: true, message: '请输入项目名称' }]"
                  class="project-field">
                </van-field>
                <van-field 
                  v-model="item.projectTypeName" 
                  is-link 
                  readonly 
                  label="项目类型" 
                  placeholder="请选择项目类型"
                  @click="openPjtType(item, index)"
                  :rules="[{ required: true, message: '请选择项目类型' }]"
                  class="project-field">
                </van-field>
                <van-field 
                  v-model="item.projectSource" 
                  label="项目来源" 
                  placeholder="请输入项目来源"
                  :rules="[{ required: true, message: '请输入项目来源' }]"
                  class="project-field">
                </van-field>
              </div>
            </div>
          </div>
        </van-cell-group>
      </van-form>
    </div>
    <footer>
      <van-button v-if="state.active > 0" @click="preStep">
        上一步
      </van-button>
      <van-button v-if="state.active < (state.form.registerType == '10' ? 1 : 2)" type="primary" class="ml10"
        @click="nextStep">
        下一步
      </van-button>
      <van-button v-if="
        (state.form.registerType == '10' && state.active === 1) ||
        (state.form.registerType == '20' && state.active === 2)
      " @click="onRegister" type="primary" class="ml10">
        提交
      </van-button>
    </footer>
    <!-- 部门选择器 -->
    <van-popup v-model:show="showDeptPicker" position="bottom">
      <div class="cascade-picker">
        <div class="cascade-header">
          <span>请选择部门</span>
          <van-icon name="cross" @click="showDeptPicker = false" />
        </div>
        <div class="cascade-content">
          <!-- 第一级 -->
          <div class="cascade-column">
            <div v-for="item in cascadeData.level1" :key="item.id" class="cascade-item"
              :class="{ active: selectedLevel1?.id === item.id }" @click="selectLevel1(item)">
              <span>{{ item.deptName }}{{ item.children?.length ? `(${item.children.length})` : '' }}</span>
              <van-icon v-if="item.children?.length" name="arrow" />
            </div>
          </div>

          <!-- 第二级 -->
          <div v-if="cascadeData.level2.length" class="cascade-column">
            <div v-for="item in cascadeData.level2" :key="item.id" class="cascade-item"
              :class="{ active: selectedLevel2?.id === item.id }" @click="selectLevel2(item)">
              <span>{{ item.deptName }}{{ item.children?.length ? `(${item.children.length})` : '' }}</span>
              <van-icon v-if="item.children?.length" name="arrow" />
            </div>
          </div>

          <!-- 第三级 -->
          <div v-if="cascadeData.level3.length" class="cascade-column">
            <div v-for="item in cascadeData.level3" :key="item.id" class="cascade-item"
              :class="{ active: selectedLevel3?.id === item.id }" @click="selectLevel3(item)">
              <span>{{ item.deptName }}</span>
            </div>
          </div>
        </div>

        <div class="cascade-footer">
          <van-button @click="showDeptPicker = false">取消</van-button>
          <van-button type="primary" @click="confirmCascadeSelection">
            确定
          </van-button>
        </div>
      </div>
    </van-popup>
    <!-- 课题组选择器 -->
    <van-popup v-model:show="showPjtPicker" position="bottom">
      <van-picker :columns="pjtList" :columns-field-names="{ text: 'pgName', value: 'id' }" @confirm="onPjtPicker"
        @cancel="showPjtPicker = false" />
    </van-popup>
    <!-- 所在时间 -->
    <van-popup v-model:show="showPjtDatePicker" position="bottom">
      <van-picker-group title="预约日期" :tabs="['开始日期', '结束日期']" next-step-text="下一步" @confirm="onPjtDatePicker">
        <van-date-picker v-model="state.form.startDate" />
        <van-date-picker v-model="state.form.endDate" />
      </van-picker-group>
    </van-popup>
    <!-- 项目类型 -->
    <van-popup v-model:show="showPjtTypePicker" position="bottom">
      <van-picker :columns="pjtTypeList" :columns-field-names="{ text: 'dictLabel', value: 'dictValue' }"
        @confirm="onPjtTypePicker" @cancel="showPjtTypePicker = false" />
    </van-popup>
    <van-notify v-model:show="show" type="danger">
      <span>操作失败</span>
    </van-notify>
  </div>
</template>

<script name="register" lang="ts" setup>
import { onMounted, reactive, ref, watch } from 'vue'
import to from 'await-to-js'
import { useLoginApi } from '/@/api/login/index'
import crypto from 'sm-crypto'
import { useDictApi } from '/@/api/system/dict'
import { useProApi } from '/@/api/project'
import { useDeptApi } from '/@/api/system/dept'
import { useRouter, useRoute } from 'vue-router'
import { showNotify } from 'vant'

import { UserTypeTooltip } from '/@/constants/pageConstants'
import { isPasswordValid } from '/@/utils/stringUtils'

const sm3 = crypto.sm3
const loginApi = useLoginApi()
const router = useRouter()
const dictApi = useDictApi()
const proApi = useProApi()
const deptApi = useDeptApi()
const loginInfoRef = ref()
const personInfoRef = ref()
const projectInfoRef = ref()
const showDeptPicker = ref(false)
const showPjtPicker = ref(false)
const showPjtDatePicker = ref(false)
const showPjtTypePicker = ref(false)
const show = ref(false)
const pjtTypeIndex = ref(-1)
const needInputDeptInfo = ref(false)
// 级联选择器相关数据
const cascadeData = ref({
  level1: <any[]>[],
  level2: <any[]>[],
  level3: <any[]>[],
})
const selectedLevel1 = ref<any>(null)
const selectedLevel2 = ref<any>(null)
const selectedLevel3 = ref<any>(null)

const userTypeList = ref(<RowDicDataType[]>[])
const userSexList = ref(<RowDicDataType[]>[])
const userCertList = ref(<RowDicDataType[]>[])
const deptData = ref(<any[]>[])
const pjtList = ref(<any[]>[])
const pjtTypeList = ref(<any[]>[])
const state = reactive({
  active: 0,
  showPassword: false,
  showConfirmPassword: false,
  loading: {
    signIn: false,
  },
  form: {
    id: 0,
    userName: '', // 账户名称
    nickName: '', // 用户姓名
    userType: '10', // 关联角色
    deptId: null,
    deptName: '', // 单位名称
    phone: '', // 手机号
    email: '', // 邮箱
    sex: '30', // 性别
    password: '', // 账户密码
    confirmPassword: '',
    status: '10', // 用户状态
    describe: '', // 用户描述
    avatar: '',
    idType: '', //证件类型
    idCode: '', // 证件号
    personnelType: '', // 人员类型
    projectId: null,
    projectName: '',
    projectDate: '',
    startDate: [],
    endDate: [],
    registerType: '10',
    applyPg: {},
    projectGroupName: '',
    projectGroupId: null,
    projectList: <any[]>[{
      projectName: '',
      projectType: '',
      projectTypeName: '',
      projectSource: '',
    }],
    unitName: '',
    deptDescribe: '',
  },
})
const deptDataBackup = ref(<any[]>[])

const getDicts = () => {
  Promise.all([
    dictApi.getDictDataByType('sys_user_type'),
    dictApi.getDictDataByType('sys_com_sex'),
    dictApi.getDictDataByType('sys_user_certificate'),
    deptApi.getDeptTree(),
    proApi.getProjectGroupListForApp({ noPage: true }),
    dictApi.getDictDataByType('sci_pjt_level'),
  ]).then(([type, sex, cert, dept, pjt, pjtType]) => {
    userTypeList.value = type.data.values || []
    userSexList.value = sex.data.values || []
    userCertList.value = cert.data.values || []
    deptDataBackup.value = dept.data || []
    deptData.value = dept.data || []
    pjtList.value = pjt.data.list || []
    pjtTypeList.value = pjtType.data.values || []

    // 初始化级联数据
    initCascadeData()
  })
}

const checkUserNamePhoneExists = async (type: 'userName' | 'phone') => {
  let resquest = loginApi.checkUserNamePhoneExists({ userName: state.form.userName, phone: '' })

  if (type === 'phone') {
    resquest = loginApi.checkUserNamePhoneExists({ userName: '', phone: state.form.phone })
  }

  await to(resquest)
}

const checkUserNameExists = (value: string) => {
  if (!value) {
    return "请输入账号"
  }
  return loginApi.checkUserNamePhoneExists({ userName: value, phone: '' })
    .then(res => {
      return true
    })
    .catch(() => {
      return "账号不合法"
    })
}

const renderDeptData = () => {
  let daptTree = deptDataBackup.value

  if (state.form.userType === '10') {
    daptTree = daptTree.filter((item) => item.id === 100001)
  }

  if (state.form.userType === '15') {
    daptTree = daptTree.filter((item) => item.id !== 1000220)
  }

  if (state.form.userType === '20') {
    daptTree = daptTree.filter((item) => item.id === 1000220)
  }

  return daptTree
}

const deptIncludesProjectGroup = async (deptId: number) => {
  const [err, res]: ToResponse = await to(
    proApi.getProjectGroupList({
      pgOrg: deptId,
      pgName: '',
      pageNum: 1,
      pageSize: 1000,
    }),
  )
  if (err) return

  pjtList.value = res?.data.list || []
}

const preStep = () => {
  state.active--
}
const validateProjectList = () => {
  // 校验至少有一个项目
  if (!state.form.projectList || state.form.projectList.length === 0) {
    showNotify({
      type: 'danger',
      message: '请至少添加一个项目',
    })
    return false
  }

  // 校验每个项目的必填字段
  for (let i = 0; i < state.form.projectList.length; i++) {
    const item = state.form.projectList[i]
    if (!item.projectName || !item.projectName.trim()) {
      showNotify({
        type: 'danger',
        message: `项目 ${i + 1} 的项目名称不能为空`,
      })
      return false
    }
    if (!item.projectType || !item.projectTypeName) {
      showNotify({
        type: 'danger',
        message: `项目 ${i + 1} 的项目类型不能为空`,
      })
      return false
    }
    if (!item.projectSource || !item.projectSource.trim()) {
      showNotify({
        type: 'danger',
        message: `项目 ${i + 1} 的项目来源不能为空`,
      })
      return false
    }
  }

  return true
}

const nextStep = async () => {
  if (state.active < 3) {
    let form = loginInfoRef.value
    if (state.active == 1) {
      form = personInfoRef.value
    } else if (state.active == 2) {
      form = projectInfoRef.value
      // 校验项目列表
      if (!validateProjectList()) {
        return
      }
      // 校验表单字段
      const [err] = await to(form.validate())
      if (err) return
      state.active++
      return
    }
    const [err, res] = await to(form.validate())
    if (err) return
    state.active++
  }
}

// const checkPassword = (value: any) => {
//   let checkResult = isPasswordValid(value)
//   if (!checkResult.isPassed) {
//     return checkResult.errorMsg
//   }
// }

const checkPassword = (value: string) => {
  if (!value) {
    return "请输入密码"
  }
  return loginApi.validatePassword({ password: value })
    .then(res => {
      return true
    })
    .catch((err) => {
      return err.message || "密码不合法"
    })
}

const confirmPasswordSame = (value: any) => {
  if (!value) {
    return false
  } else if (value !== state.form.password) {
    return false
  } else {
    return true
  }
}

watch(
  () => state.form.userType,
  (newVal) => {
    if (newVal === '20') {
      state.form.idType = '30'
      needInputDeptInfo.value = true
    } else {
      state.form.idType = '20'
      state.form.deptId = null
      state.form.deptName = ''
      needInputDeptInfo.value = false
    }
  },
  { immediate: true },
)

const changeType = (val: string) => {
  state.form.registerType = val
  if (val === '10' && state.active == 2) {
    state.active = 1
  }
}

const showDeptPickerHandler = () => {
  initCascadeData()
  showDeptPicker.value = true
}
const onPjtPicker = ({ selectedOptions }: { selectedOptions: any[] }) => {
  showPjtPicker.value = false
  state.form.projectGroupName = selectedOptions[0].pgName
  state.form.projectGroupId = selectedOptions[0].id
}
const onPjtDatePicker = () => {
  showPjtDatePicker.value = false
  state.form.projectDate = `${state.form.startDate.join('-')}至${state.form.endDate.join('-')}`
}
const addProject = () => {
  state.form.projectList.push({
    projectName: '',
    projectType: '',
    projectTypeName: '',
    projectSource: '',
  })
}
const delProjectItem = (idx: number) => {
  if (state.form.projectList.length > 1) {
    state.form.projectList.splice(idx, 1)
  } else {
    showNotify({
      type: 'danger',
      message: '至少需要保留一个项目',
    })
  }
}
const openPjtType = (row: any, idx: number) => {
  pjtTypeIndex.value = idx
  showPjtTypePicker.value = true
}
const onPjtTypePicker = ({ selectedOptions }: { selectedOptions: any[] }) => {
  showPjtTypePicker.value = false
  state.form.projectList[pjtTypeIndex.value].projectType = selectedOptions[0].dictValue
  state.form.projectList[pjtTypeIndex.value].projectTypeName = selectedOptions[0].dictLabel
}

watch(
  () => state.form.deptId,
  (newVal) => {
    if (newVal) {
      deptIncludesProjectGroup(newVal)
    }
  },
)

// 级联选择器方法
const initCascadeData = () => {
  cascadeData.value.level1 = renderDeptData()
  cascadeData.value.level2 = []
  cascadeData.value.level3 = []
  selectedLevel1.value = null
  selectedLevel2.value = null
  selectedLevel3.value = null
}

const selectLevel1 = (item: any) => {
  selectedLevel1.value = item
  selectedLevel2.value = null
  selectedLevel3.value = null

  if (item.children && item.children.length > 0) {
    cascadeData.value.level2 = item.children
    cascadeData.value.level3 = []
  } else {
    cascadeData.value.level2 = []
    cascadeData.value.level3 = []
  }
}

const selectLevel2 = (item: any) => {
  selectedLevel2.value = item
  selectedLevel3.value = null

  if (item.children && item.children.length > 0) {
    cascadeData.value.level3 = item.children
  } else {
    cascadeData.value.level3 = []
  }
}

const selectLevel3 = (item: any) => {
  selectedLevel3.value = item
}

const confirmCascadeSelection = () => {
  const selectedItem = selectedLevel3.value || selectedLevel2.value || selectedLevel1.value
  if (selectedItem) {
    state.form.deptName = selectedItem.deptName
    state.form.deptId = selectedItem.id
    showDeptPicker.value = false
  }
}

const onRegister = async () => {
  const form = state.form.registerType == '10' ? personInfoRef.value : personInfoRef.value
  const [error] = await to(form.validate())
  if (error) return
  
  // 如果是课题组负责人，需要校验项目信息
  if (state.form.registerType === '20') {
    if (!validateProjectList()) {
      return
    }
  }
  
  const params = JSON.parse(JSON.stringify(state.form))
  params.password = sm3(params.password)
  delete params.confirmPassword
  params.startDate = params.startDate.join('-')
  params.endDate = params.endDate.join('-')
  delete params.projectDate
  const [err] = await to(loginApi.register(params))
  if (err) {
    // show.value = true
    // setTimeout(() => {
    //   show.value = false
    // }, 2000)
    return
  }
  showNotify({
    type: 'success',
    message:
      state.form.registerType === '20'
        ? '你已经注册为课题组负责人，等待系统管理员审核通过'
        : '你已经注册为课题组成员，等待课题组负责人审核通过',
  })
  router.push('/login')
}
onMounted(async () => {
  getDicts()
})
</script>

<style lang="scss" scoped>
.register {
  display: flex;
  flex-direction: column;
  height: calc(100% - 20px);

  .van-row .van-button {
    flex: 1;

    &+.van-button {
      margin-left: 10px;
    }
  }

  .form {
    flex: 1;
    overflow-y: auto;
  }

  footer {
    padding: 0 10px 10px;
    flex: 0 0 40px;
    display: flex;
    justify-content: flex-end;
  }
}

.cascade-picker {
  background: #fff;
  border-radius: 8px 8px 0 0;
  overflow: hidden;

  .cascade-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px;
    border-bottom: 1px solid #f5f5f5;
    font-size: 16px;
    font-weight: 500;

    .van-icon {
      font-size: 18px;
      color: #969799;
      cursor: pointer;
    }
  }

  .cascade-content {
    display: flex;
    height: 300px;

    .cascade-column {
      flex: 1;
      border-right: 1px solid #f5f5f5;
      overflow-y: auto;

      &:last-child {
        border-right: none;
      }

      .cascade-item {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 12px 16px;
        cursor: pointer;
        transition: background-color 0.2s;

        &:hover {
          background-color: #f7f8fa;
        }

        &.active {
          background-color: #e8f3ff;
          color: #1989fa;
        }

        span {
          flex: 1;
          font-size: 14px;
        }

        .van-icon {
          font-size: 12px;
          color: #c8c9cc;
        }
      }
    }
  }

  .cascade-footer {
    display: flex;
    padding: 12px 16px;
    border-top: 1px solid #f5f5f5;
    gap: 12px;

    .van-button {
      flex: 1;
    }
  }
}

.project-list-container {
  padding: 12px;

  .add-project-btn {
    margin-bottom: 12px;
  }

  .empty-project-tip {
    padding: 20px;
    text-align: center;
    color: #969799;
    font-size: 14px;
    background-color: #f7f8fa;
    border-radius: 8px;
    margin-bottom: 16px;
  }

  .project-card {
    background: #fff;
    border: 1px solid #ebedf0;
    border-radius: 8px;
    margin-bottom: 16px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
    transition: box-shadow 0.3s;

    &:hover {
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    }

    .project-card-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 12px 16px;
      background: linear-gradient(135deg, #1989fa 0%, #0050b3 100%);
      color: #fff;

      .project-card-title {
        font-size: 16px;
        font-weight: 500;
      }

      .delete-btn {
        background: rgba(255, 255, 255, 0.2);
        border-color: rgba(255, 255, 255, 0.3);
        color: #fff;

        &:hover {
          background: rgba(255, 255, 255, 0.3);
        }
      }
    }

    .project-card-body {
      padding: 12px 0;

      .project-field {
        margin-bottom: 8px;

        &:last-child {
          margin-bottom: 0;
        }
      }
    }
  }
}
</style>
