index.vue 5.58 KB
<template>
  <div class="teacher-form-page">
    <van-config-provider :theme-vars="themeVars">
      <div class="form-card">
        <header class="page-header">
          <h1>老师表单录入</h1>
          <p>请填写老师报名信息。</p>
        </header>

        <van-form
          scroll-to-error
          scroll-to-error-position="center"
          validate-first
          @submit="saveForm"
          @failed="handleSubmitFailed"
        >
          <section class="form-section">
            <div class="field-block required">
              <div class="field-label">姓名</div>
              <van-field
                v-model="form.name"
                name="name"
                placeholder="请输入姓名"
                :rules="[{ required: true, message: '请输入姓名' }]"
              />
            </div>

            <div class="field-block required">
              <div class="field-label">身份证</div>
              <van-field
                v-model="form.id_card"
                name="id_card"
                maxlength="18"
                placeholder="请输入身份证号码"
                :rules="[
                  { required: true, message: '请输入身份证号码' },
                  { validator: idCardValidator, message: '请输入正确身份证号码' }
                ]"
              />
            </div>

            <div class="field-block required">
              <div class="field-label">尺寸</div>
              <van-field
                v-model="form.size_name"
                readonly
                is-link
                name="size_name"
                placeholder="请选择尺寸"
                :rules="[{ required: true, message: '请选择尺寸' }]"
                @click="showSizePicker = true"
              />
            </div>
          </section>

          <div class="submit-bar">
            <van-button block round type="primary" native-type="submit" :loading="saveLoading">保存</van-button>
          </div>
        </van-form>
      </div>

      <van-popup v-model:show="showSizePicker" round position="bottom">
        <van-picker
          :columns="sizeColumns"
          @cancel="showSizePicker = false"
          @confirm="onSizeConfirm"
        />
      </van-popup>
    </van-config-provider>
  </div>
</template>

<script setup>
import { onMounted, reactive, ref } from 'vue';
import { showDialog, showFailToast, showSuccessToast } from 'vant';
import { styleColor } from '@/constant.js';
import { submitTeacherRegistration } from './service';

const themeVars = {
  buttonPrimaryBackground: styleColor.baseColor,
  buttonPrimaryBorderColor: styleColor.baseColor,
  buttonPrimaryColor: styleColor.baseFontColor
};

const sizeColumns = [
  { text: '成年XS码', value: 'adult_xs' },
  { text: '成年S码', value: 'adult_s' },
  { text: '成年M码', value: 'adult_m' },
  { text: '成年L码', value: 'adult_l' }
];

const form = reactive({
  name: '',
  id_card: '',
  size: '',
  size_name: ''
});

const showSizePicker = ref(false);
const saveLoading = ref(false);

const idCardValidator = (value) => {
  if (!/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(value)) {
    return false;
  }

  const factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
  const checks = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
  const sum = value
    .slice(0, 17)
    .split('')
    .reduce((total, item, index) => total + Number(item) * factors[index], 0);

  return checks[sum % 11] === value[17].toUpperCase();
};

const onSizeConfirm = ({ selectedOptions }) => {
  const size = selectedOptions[0];
  showSizePicker.value = false;
  form.size = size.value;
  form.size_name = size.text;
};

const saveForm = async () => {
  saveLoading.value = true;
  try {
    await submitTeacherRegistration({ ...form });
    showSuccessToast('保存成功');
  } catch (error) {
    showFailToast(error.message);
  } finally {
    saveLoading.value = false;
  }
};

const handleSubmitFailed = ({ errors = [] }) => {
  const message = errors.find((item) => item.message)?.message || '请检查老师表单信息';

  showDialog({
    title: '请完善老师信息',
    message,
    confirmButtonColor: styleColor.baseColor
  });
};

onMounted(() => {
  document.title = '老师表单录入';
});
</script>

<style lang="less" scoped>
.teacher-form-page {
  min-height: 100vh;
  padding: 16px;
  background: #f3f5f7;
  box-sizing: border-box;
}

.form-card {
  min-height: calc(100vh - 32px);
  padding: 22px 14px 28px;
  background: #ffffff;
  border-radius: 16px;
  box-sizing: border-box;
}

.page-header {
  margin-bottom: 42px;

  h1 {
    margin: 0;
    color: #101010;
    font-size: 22px;
    line-height: 1.35;
    font-weight: 700;
  }

  p {
    margin: 14px 0 0;
    color: #8a8f96;
    font-size: 14px;
    line-height: 1.6;
  }
}

.form-section {
  margin-bottom: 36px;
}

.field-block {
  margin-bottom: 30px;

  :deep(.van-cell) {
    min-height: 46px;
    padding: 0 14px;
    align-items: center;
    border: 1px solid #e8e8e8;
    border-radius: 6px;
    box-shadow: none;
  }

  :deep(.van-field__control) {
    color: #222222;
    font-size: 14px;
  }

  :deep(.van-field__control::placeholder) {
    color: #b9bec3;
  }

  :deep(.van-field__error-message) {
    padding-top: 4px;
  }
}

.field-label {
  position: relative;
  margin-bottom: 14px;
  color: #101010;
  font-size: 17px;
  line-height: 1.4;
  font-weight: 700;
}

.required .field-label {
  padding-left: 14px;

  &::before {
    position: absolute;
    left: 0;
    top: 1px;
    color: #ee3f3f;
    content: '*';
  }
}

.submit-bar {
  padding-top: 4px;
}
</style>