|
|
@@ -0,0 +1,472 @@
|
|
|
+<template>
|
|
|
+ <div class="maintenance-apply-container">
|
|
|
+ <div class="page-header">
|
|
|
+ <div class="header-left">维保申请</div>
|
|
|
+ <div class="header-right" @click="handleBack">返回</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="content-wrapper">
|
|
|
+ <div class="custom-steps-container">
|
|
|
+ <div class="step-list">
|
|
|
+ <div class="step-item active">
|
|
|
+ <div class="step-icon">
|
|
|
+ <el-icon><Check /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="step-title">提交维保申请</div>
|
|
|
+ </div>
|
|
|
+ <div class="step-line active-line"></div>
|
|
|
+ <div class="step-item">
|
|
|
+ <div class="step-icon normal">
|
|
|
+ <span>2</span>
|
|
|
+ </div>
|
|
|
+ <div class="step-title normal-text">审核通过开始服务</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="form-container">
|
|
|
+ <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="apply-form" hide-required-asterisk>
|
|
|
+ <el-form-item label="申请人名称:" prop="applicantName">
|
|
|
+ <el-input v-model="form.applicantName" placeholder="请输入申请人名称" clearable />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- The mockup shows a validation error explicitly for phone -->
|
|
|
+ <el-form-item label="手机号码:" prop="phone" class="phone-item">
|
|
|
+ <el-input v-model="form.phone" placeholder="请输入申请人手机号码" clearable />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="验证码:" prop="verifyCode">
|
|
|
+ <div class="verify-code-wrapper">
|
|
|
+ <el-input v-model="form.verifyCode" placeholder="短信验证码" class="code-input" clearable />
|
|
|
+ <div class="send-btn" @click="handleSendCode">发送验证码</div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="">
|
|
|
+ <div class="drag-verify-bar" @click="handleVerify">
|
|
|
+ <div class="verify-btn" :class="{ verified: isVerified }">
|
|
|
+ <div class="inner-dot" v-if="!isVerified"></div>
|
|
|
+ <el-icon v-else color="#0fb881"><Check /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="verify-text">{{ isVerified ? '验证通过' : '点击验证' }}</div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="服务时间:" prop="serviceTime">
|
|
|
+ <el-select v-model="form.serviceTime" placeholder="请选择服务时间" class="w-full" filterable>
|
|
|
+ <el-option v-for="dict in service_time_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="月度维保次数:" prop="monthlyTimes">
|
|
|
+ <el-input v-model="form.monthlyTimes" placeholder="请输入月度维保次数" clearable />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="维保类型:" prop="maintainType">
|
|
|
+ <el-select v-model="form.maintainType" placeholder="请选择" class="w-full" filterable>
|
|
|
+ <el-option v-for="dict in maintenance_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="服务内容:" prop="serviceContent">
|
|
|
+ <div class="service-content-wrapper">
|
|
|
+ <div class="service-tags">
|
|
|
+ <div class="tag-item" :class="{ active: form.serviceContent.includes('express') }" @click="toggleServiceContent('express')">
|
|
|
+ 急速快递
|
|
|
+ </div>
|
|
|
+ <div class="tag-item" :class="{ active: form.serviceContent.includes('repair') }" @click="toggleServiceContent('repair')">
|
|
|
+ 售后维修
|
|
|
+ </div>
|
|
|
+ <div class="tag-item" :class="{ active: form.serviceContent.includes('warranty') }" @click="toggleServiceContent('warranty')">
|
|
|
+ 产品质保
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-input v-model="form.otherService" type="textarea" :rows="4" placeholder="请输入其他服务" class="other-service-input" />
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" class="next-btn" @click="handleNext">下一步</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, reactive, onMounted } from 'vue';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+import { Check } from '@element-plus/icons-vue';
|
|
|
+import { ElMessage } from 'element-plus';
|
|
|
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
+const { service_time_type, maintenance_type } = toRefs<any>(proxy?.useDict('service_time_type', 'maintenance_type'));
|
|
|
+
|
|
|
+const router = useRouter();
|
|
|
+const formRef = ref();
|
|
|
+
|
|
|
+const isVerified = ref(false);
|
|
|
+
|
|
|
+const form = reactive({
|
|
|
+ applicantName: '',
|
|
|
+ phone: '',
|
|
|
+ verifyCode: '',
|
|
|
+ serviceTime: '',
|
|
|
+ monthlyTimes: '',
|
|
|
+ maintainType: '',
|
|
|
+ serviceContent: [] as string[],
|
|
|
+ otherService: ''
|
|
|
+});
|
|
|
+
|
|
|
+// To perfectly match the mockup which shows a persistent validation error on phone
|
|
|
+const validatePhone = (rule: any, value: string, callback: any) => {
|
|
|
+ if (!value) {
|
|
|
+ callback(new Error('请输入手机号码'));
|
|
|
+ } else if (!/^1[3-9]\d{9}$/.test(value)) {
|
|
|
+ callback(new Error('手机号码格式不正确'));
|
|
|
+ } else {
|
|
|
+ callback();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const rules = {
|
|
|
+ applicantName: [{ required: true, message: '请输入申请人名称', trigger: 'blur' }],
|
|
|
+ phone: [{ required: true, validator: validatePhone, trigger: 'blur' }],
|
|
|
+ verifyCode: [{ required: true, message: '请输入短信验证码', trigger: 'blur' }],
|
|
|
+ serviceTime: [{ required: true, message: '请选择服务时间', trigger: 'change' }],
|
|
|
+ monthlyTimes: [{ required: true, message: '请输入月度维保次数', trigger: 'blur' }],
|
|
|
+ maintainType: [{ required: true, message: '请选择维保类型', trigger: 'change' }]
|
|
|
+};
|
|
|
+
|
|
|
+const handleBack = () => {
|
|
|
+ router.back();
|
|
|
+};
|
|
|
+
|
|
|
+const handleSendCode = () => {
|
|
|
+ if (!form.phone) {
|
|
|
+ ElMessage.warning('请先输入手机号码');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ElMessage.success('验证码已发送');
|
|
|
+};
|
|
|
+
|
|
|
+const handleVerify = () => {
|
|
|
+ isVerified.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+const toggleServiceContent = (type: string) => {
|
|
|
+ const index = form.serviceContent.indexOf(type);
|
|
|
+ if (index > -1) {
|
|
|
+ form.serviceContent.splice(index, 1);
|
|
|
+ } else {
|
|
|
+ form.serviceContent.push(type);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const handleNext = async () => {
|
|
|
+ if (!formRef.value) return;
|
|
|
+ await formRef.value.validate((valid: boolean) => {
|
|
|
+ if (valid) {
|
|
|
+ if (!isVerified.value) {
|
|
|
+ ElMessage.warning('请先点击验证');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ElMessage.success('提交成功');
|
|
|
+ // router.push('...');
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ // To trigger the phone error message immediately as shown in the mockup
|
|
|
+ setTimeout(() => {
|
|
|
+ if (formRef.value) {
|
|
|
+ formRef.value.validateField('phone').catch(() => {});
|
|
|
+ }
|
|
|
+ }, 500);
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.maintenance-apply-container {
|
|
|
+ min-height: 100vh;
|
|
|
+ background-color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.page-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0 40px;
|
|
|
+ height: 60px;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+
|
|
|
+ .header-left {
|
|
|
+ font-size: 16px;
|
|
|
+ color: #333;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .header-right {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.content-wrapper {
|
|
|
+ max-width: 800px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 40px 20px;
|
|
|
+}
|
|
|
+
|
|
|
+/* Steps Styling */
|
|
|
+.custom-steps-container {
|
|
|
+ margin-bottom: 60px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ .step-list {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ width: 600px;
|
|
|
+ justify-content: space-between;
|
|
|
+ }
|
|
|
+
|
|
|
+ .step-item {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ position: relative;
|
|
|
+ z-index: 2;
|
|
|
+ background: #fff;
|
|
|
+ padding: 0 10px;
|
|
|
+ width: 140px;
|
|
|
+
|
|
|
+ .step-icon {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 20px;
|
|
|
+ border: 1px solid #e60012;
|
|
|
+ color: #e60012;
|
|
|
+ background-color: #fff;
|
|
|
+ margin-bottom: 10px;
|
|
|
+
|
|
|
+ &.normal {
|
|
|
+ border-color: #d9d9d9;
|
|
|
+ color: #b3b3b3;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .step-title {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #e60012;
|
|
|
+ font-weight: 500;
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ &.normal-text {
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .step-line {
|
|
|
+ flex: 1;
|
|
|
+ height: 2px;
|
|
|
+ background-color: #e60012;
|
|
|
+ margin: 0 -20px;
|
|
|
+ margin-top: 19px;
|
|
|
+ z-index: 1;
|
|
|
+
|
|
|
+ &.active-line {
|
|
|
+ background-color: #e60012;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Form Styling */
|
|
|
+.form-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ .apply-form {
|
|
|
+ width: 500px;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-form-item__label) {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #333;
|
|
|
+ font-weight: normal;
|
|
|
+ }
|
|
|
+
|
|
|
+ .full-width {
|
|
|
+ width: 200px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.verify-code-wrapper {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
|
|
|
+
|
|
|
+ &:focus-within {
|
|
|
+ border-color: #409eff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .code-input {
|
|
|
+ flex: 1;
|
|
|
+ :deep(.el-input__wrapper) {
|
|
|
+ border-radius: 0;
|
|
|
+ box-shadow: none !important;
|
|
|
+ background-color: transparent;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .send-btn {
|
|
|
+ padding: 0 15px;
|
|
|
+ color: #666;
|
|
|
+ font-size: 13px;
|
|
|
+ cursor: pointer;
|
|
|
+ border-left: 1px solid #dcdfe6;
|
|
|
+ background: #fafafa;
|
|
|
+ height: 32px;
|
|
|
+ line-height: 32px;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.drag-verify-bar {
|
|
|
+ width: 100%;
|
|
|
+ height: 34px;
|
|
|
+ background-color: #f0f5ff;
|
|
|
+ border: 1px solid #c6d8f5;
|
|
|
+ border-radius: 4px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ .verify-btn {
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 50%;
|
|
|
+ border: 1px solid #c6d8f5;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ margin-left: -1px;
|
|
|
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.05);
|
|
|
+ position: absolute;
|
|
|
+ left: 10px;
|
|
|
+
|
|
|
+ .inner-dot {
|
|
|
+ width: 10px;
|
|
|
+ height: 10px;
|
|
|
+ background-color: #a4c2f4;
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.verified {
|
|
|
+ left: auto;
|
|
|
+ right: -1px;
|
|
|
+ border-color: #0fb881;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .verify-text {
|
|
|
+ flex: 1;
|
|
|
+ text-align: center;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #666;
|
|
|
+ user-select: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.success {
|
|
|
+ background-color: #e5f7f1;
|
|
|
+ border-color: #0fb881;
|
|
|
+
|
|
|
+ .verify-text {
|
|
|
+ color: #0fb881;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.service-content-wrapper {
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ .service-tags {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 10px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ .tag-item {
|
|
|
+ padding: 6px 16px;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #666;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ border-color: #409eff;
|
|
|
+ color: #409eff;
|
|
|
+ background-color: #ecf5ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #c0c4cc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .other-service-input {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.next-btn {
|
|
|
+ background-color: #0fb881;
|
|
|
+ border-color: #0fb881;
|
|
|
+ color: #fff;
|
|
|
+ padding: 8px 25px;
|
|
|
+ border-radius: 2px;
|
|
|
+ font-weight: normal;
|
|
|
+
|
|
|
+ &:hover,
|
|
|
+ &:focus {
|
|
|
+ background-color: #12cc90;
|
|
|
+ border-color: #12cc90;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Override error styles to match exactly the mockup */
|
|
|
+:deep(.phone-item.is-error) {
|
|
|
+ .el-input__wrapper {
|
|
|
+ box-shadow: 0 0 0 1px #f56c6c inset !important;
|
|
|
+ }
|
|
|
+ .el-form-item__error {
|
|
|
+ color: #e60012; /* matching the red error text color in the mockup */
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|