律师系统前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1065 lines
28 KiB

3 months ago
<template>
<div class="mobile-create-page">
<!-- 头部导航 -->
<div class="page-header">
<div class="header-left">
<button class="back-btn" @click="handleBack">
<span></span>
</button>
</div>
<div class="header-title">
新建申报
</div>
<div class="header-right">
<!-- 占位 -->
</div>
</div>
<!-- 表单内容 -->
<div class="form-content">
<!-- 基础信息 -->
<div class="form-section">
<div class="section-title">基础信息</div>
<!-- 姓名 -->
<div class="form-item">
<label class="form-label">姓名</label>
<input
v-model="form.actualName"
class="form-input"
placeholder="员工姓名"
disabled
/>
</div>
<!-- 执业证号 -->
<div class="form-item">
<label class="form-label">执业证号</label>
<input
v-model="form.certificateNumber"
class="form-input"
placeholder="执业证号"
disabled
/>
</div>
<!-- 执业机构 -->
<div class="form-item">
<label class="form-label">执业机构</label>
<input
v-model="form.firmName"
class="form-input"
placeholder="执业机构名称"
disabled
/>
</div>
<!-- 职务 -->
<div class="form-item">
<label class="form-label required">职务</label>
<select
v-model="form.positionId"
class="form-select"
>
<option value="">请选择职务</option>
<option
v-for="position in positionList"
:key="position.positionId"
:value="position.positionId"
>
{{ position.positionName }}
</option>
</select>
</div>
</div>
<!-- 服务信息 -->
<div class="form-section">
<div class="section-title">服务信息</div>
<!-- 服务开始时间 -->
<div class="form-item">
<label class="form-label required">服务开始时间</label>
<DatePicker
v-model:value="form.serviceStart"
:show-time="{ format: 'HH:00' }"
format="YYYY-MM-DD HH:00"
value-format="YYYY-MM-DD HH:00:00"
placeholder="请选择服务开始时间"
class="form-date-picker"
/>
</div>
<!-- 服务结束时间 -->
<div class="form-item">
<label class="form-label required">服务结束时间</label>
<DatePicker
v-model:value="form.serviceEnd"
:show-time="{ format: 'HH:00' }"
format="YYYY-MM-DD HH:00"
value-format="YYYY-MM-DD HH:00:00"
placeholder="请选择服务结束时间"
class="form-date-picker"
/>
</div>
<!-- 服务时长 -->
<div class="form-item">
<label class="form-label required">
服务时长小时
<span class="label-tip">可通过时间选择也可手动填写</span>
</label>
<input
type="number"
v-model="form.serviceDuration"
class="form-input"
placeholder="服务时长(小时)"
/>
<div class="form-tip">:不足30分钟不含本数不计入时长超过30分钟不足1个小时的含本数按照一个小时填报</div>
</div>
</div>
<!-- 活动信息 -->
<div class="form-section">
<div class="section-title">活动信息</div>
<!-- 活动类型 -->
<div class="form-item">
<label class="form-label required">活动类型</label>
<select
v-model="form.activityCategoryId"
class="form-select"
@change="onActivityCategoryChange"
>
<option value="">请选择活动类型</option>
<option
v-for="category in activityCategoryList"
:key="category.categoryId"
:value="category.categoryId"
>
{{ category.categoryName }}
</option>
</select>
</div>
<!-- 活动名称 -->
<div class="form-item">
<label class="form-label required">活动名称</label>
<select
v-model="form.activityNameId"
class="form-select"
>
<option value="">请选择活动名称</option>
<option
v-for="activity in activityList"
:key="activity.goodsId"
:value="activity.goodsId"
>
{{ activity.goodsName }}
</option>
</select>
</div>
<!-- 参加人数受益人数 -->
<div class="form-item">
<label class="form-label required">参加人数受益人数</label>
<input
type="number"
v-model="form.beneficiaryCount"
class="form-input"
placeholder="参加人数(受益人数)"
/>
</div>
<!-- 组织单位名称 -->
<div class="form-item">
<label class="form-label required">组织单位名称</label>
<input
v-model="form.organizerName"
class="form-input"
placeholder="组织单位名称"
/>
</div>
<!-- 服务对象负责人/联系人姓名 -->
<div class="form-item">
<label class="form-label required">服务对象负责人/联系人姓名</label>
<input
v-model="form.organizerContact"
class="form-input"
placeholder="服务对象负责人/联系人姓名"
/>
</div>
<!-- 联系方式 -->
<div class="form-item">
<label class="form-label required">联系方式</label>
<input
v-model="form.organizerPhone"
class="form-input"
placeholder="联系方式"
/>
</div>
<!-- 服务内容 -->
<div class="form-item">
<label class="form-label required">服务内容描述</label>
<textarea
v-model="form.serviceContent"
class="form-textarea"
placeholder="请输入服务内容描述"
rows="4"
></textarea>
</div>
</div>
<!-- 证明材料 -->
<div class="form-section">
<div class="section-title">证明材料</div>
<div class="form-item">
<label class="form-label required">上传证明材料最多5个文件</label>
<input
type="file"
class="file-input"
multiple
accept=".jpg,.jpeg,.png,.pdf,.doc,.docx,.ppt,.pptx"
@change="handleFileUpload"
:disabled="uploadedFiles.length >= 5"
/>
<div class="form-tip">
:请上传活动方案活动记录照片新闻报道等材料支持图片(JPG/PNG)文档(PDF/Word/PPT)格式单文件最大10MB最多上传5个文件
</div>
<!-- 文件列表 -->
<div class="file-list" v-if="uploadedFiles.length > 0">
<div
v-for="file in uploadedFiles"
:key="file.id"
class="file-item"
>
<span class="file-name">{{ file.name || file.fileName || '未命名文件' }}</span>
<button
class="remove-btn"
@click="removeFile(file.id)"
v-if="!readonlyMode"
>
删除
</button>
</div>
</div>
<div v-else-if="form.attachmentIds && form.attachmentIds.trim()">
<div class="form-tip">证明材料已上传但文件列表为空</div>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<button
class="btn btn-secondary"
@click="handleSave"
:disabled="loading"
>
保存草稿
</button>
<button
class="btn btn-primary"
@click="handleSubmit"
:disabled="loading"
>
提交申报
</button>
</div>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading-overlay">
<div class="loading-spinner">处理中...</div>
</div>
</div>
</template>
<script setup>
3 months ago
import { ref, reactive, onMounted, watch, nextTick } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { serviceApplicationsApi } from '/@/api/business/service-applications/service-applications-api'
import { loginApi } from '/@/api/system/login-api'
import { positionApi } from '/@/api/system/position-api' // 引入职务API
3 months ago
import { categoryApi } from '/@/api/business/category/category-api' // 引入分类API
import { goodsApi } from '/@/api/business/goods/goods-api' // 引入活动API
import { fileApi } from '/@/api/support/file-api' // 引入文件上传API
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const' // 引入文件文件夹类型常量
import { message, DatePicker } from 'ant-design-vue'
import dayjs from 'dayjs'
const router = useRouter()
3 months ago
const route = useRoute()
const loading = ref(false)
3 months ago
// 表单验证规则(与PC端相同)
const rules = {
positionId: [{ required: true, message: '职务 必填' }],
serviceStart: [{ required: true, message: '服务开始时间 必填' }],
serviceEnd: [{ required: true, message: '服务结束时间 必填' }],
serviceDuration: [{ required: true, message: '服务时长(小时) 必填' }],
activityCategoryId: [{ required: true, message: '活动类型 必填' }],
activityNameId: [{ required: true, message: '活动名称 必填' }],
beneficiaryCount: [{ required: true, message: '参加人数(受益人数) 必填' }],
organizerName: [{ required: true, message: '组织单位名称 必填' }],
organizerContact: [{ required: true, message: '负责人姓名 必填' }],
organizerPhone: [
{ required: true, message: '联系方式 必填' },
{
pattern: /^(1[3-9]\d{9}|[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/,
message: '请输入正确的手机号或邮箱格式'
}
],
serviceContent: [{ required: true, message: '服务内容描述 必填' }]
}
// 日期时间选择器配置(与PC端相同)
const datePickerProps = {
showTime: true,
format: 'YYYY-MM-DD HH:00:00',
valueFormat: 'YYYY-MM-DD HH:00:00',
placeholder: '请选择时间',
style: { width: '100%' },
}
// 表单数据(与PC端完全一致)
3 months ago
const formDefault = {
applicationId: undefined, //申报ID
userId: undefined, //姓名ID
actualName: undefined, //员工姓名
certificateNumber: undefined,
firmId: undefined, //执业机构ID
departmentName: undefined, //部门名称
serviceStart: undefined, //服务开始时间
serviceEnd: undefined, //服务结束时间
serviceDuration: undefined, //服务时长(小时)
activityCategoryId: undefined, //活动类型
activityNameId: undefined, //活动名称
beneficiaryCount: undefined, //参加人数(受益人数)
organizerName: undefined, //组织单位名称
organizerContact: undefined, //负责人姓名
organizerPhone: undefined, //联系方式
serviceContent: undefined, //服务内容描述
proofMaterials: undefined, //证明材料
attachmentIds: undefined, //附件ID列表(逗号隔开)
firmAuditStatus: undefined, //执业机构审核状态:0-待审核,1-通过,2-退回
recordStatus: undefined, //备案状态:0-未备案,1-已备案
updateTime: undefined, //更新时间
createTime: undefined, //创建时间
};
const form = reactive({ ...formDefault })
// 监听服务开始时间和结束时间变化,自动计算服务时长(与PC端相同逻辑)
watch(
() => [form.serviceStart, form.serviceEnd],
([startTime, endTime]) => {
if (startTime && endTime) {
const start = dayjs(startTime);
const end = dayjs(endTime);
if (end.isAfter(start)) {
// 计算时间差(分钟),然后转换为小时(保留2位小数)
const durationMinutes = end.diff(start, 'minute');
form.serviceDuration = parseFloat((durationMinutes / 60).toFixed(2));
} else {
form.serviceDuration = undefined;
message.warning('服务结束时间必须晚于开始时间');
}
} else {
form.serviceDuration = undefined;
}
},
{ immediate: true }
);
// 下拉选项数据
const positionList = ref([])
const activityCategoryList = ref([])
const activityList = ref([])
const uploadedFiles = ref([])
// 获取用户信息并填充表单
async function getUserInfo() {
try {
const res = await loginApi.getLoginInfo()
const userInfo = res.data
// 填充用户信息(与PC端逻辑一致)
3 months ago
form.userId = userInfo.employeeId // 申报律师ID
form.actualName = userInfo.actualName || ''
form.certificateNumber = userInfo.licenseNumber || ''
3 months ago
form.firmId = userInfo.departmentId // 执业机构ID
form.firmName = userInfo.departmentName || ''
} catch (error) {
console.error('获取用户信息失败:', error)
}
}
// 获取下拉选项数据
async function getSelectOptions() {
try {
// 获取职务列表 - 使用现有的接口
3 months ago
const positionRes = await positionApi.queryList()
positionList.value = positionRes.data || []
// 获取活动类型列表 - 使用现有的接口
3 months ago
const categoryRes = await categoryApi.queryCategoryTree({
categoryType: 1 // 商品分类
})
activityCategoryList.value = categoryRes.data || []
} catch (error) {
console.error('获取选项数据失败:', error)
}
}
// 活动类型改变事件
async function onActivityCategoryChange() {
if (form.activityCategoryId) {
try {
// 根据活动类型获取活动名称列表 - 使用现有的接口
3 months ago
const activityRes = await goodsApi.queryGoodsList({
categoryId: form.activityCategoryId,
shelvesFlag: true, // 只查询上架的活动
pageNum: 1,
pageSize: 100 // 获取足够多的数据
})
3 months ago
activityList.value = activityRes.data?.list || []
} catch (error) {
console.error('获取活动列表失败:', error)
activityList.value = []
}
} else {
activityList.value = []
}
form.activityNameId = ''
}
// 文件上传处理
3 months ago
async function handleFileUpload(event) {
const files = event.target.files
if (files.length + uploadedFiles.value.length > 5) {
message.error('最多只能上传5个文件')
return
}
for (let file of files) {
if (file.size > 10 * 1024 * 1024) {
message.error(`文件 ${file.name} 超过10MB限制`)
continue
}
3 months ago
// 立即上传文件
try {
const fileFormData = new FormData()
fileFormData.append('file', file)
console.log('开始上传文件:', file.name)
const uploadRes = await fileApi.uploadFile(fileFormData, FILE_FOLDER_TYPE_ENUM.COMMON.value)
if (uploadRes.code === 0 && uploadRes.data) {
console.log('文件上传成功:', file.name, '文件ID:', uploadRes.data.fileId)
uploadedFiles.value.push({
id: uploadRes.data.fileId,
name: file.name,
file: file,
fileId: uploadRes.data.fileId,
status: 'success'
})
message.success(`文件 ${file.name} 上传成功`)
} else {
throw new Error(uploadRes.msg || '上传失败')
}
} catch (error) {
console.error('文件上传失败:', file.name, error)
message.error(`文件 ${file.name} 上传失败: ${error.message}`)
}
}
// 清空input
event.target.value = ''
}
// 删除文件
function removeFile(fileId) {
uploadedFiles.value = uploadedFiles.value.filter(file => file.id !== fileId)
}
// 保存草稿
async function handleSave() {
loading.value = true
try {
3 months ago
// 1. 获取已上传文件的ID列表
const fileIds = uploadedFiles.value.map(file => file.id).filter(id => id)
console.log('保存草稿,文件ID列表:', fileIds)
// 2. 调用保存草稿接口 - 使用现有的接口
const res = await serviceApplicationsApi.add({
...form,
3 months ago
attachmentIds: fileIds.join(','), // 将文件ID列表转换为逗号分隔的字符串
// 添加必要的参数
status: 4 // 草稿状态
})
if (res.code === 0) {
message.success('保存成功')
router.push('/mobile/service')
} else {
message.error(res.msg || '保存失败')
}
} catch (error) {
console.error('保存失败:', error)
message.error('保存失败,请稍后重试')
} finally {
loading.value = false
}
}
// 提交申报
async function handleSubmit() {
// 表单验证(与PC端相同的验证逻辑)
if (!form.positionId) {
message.error('请选择职务')
return
}
if (!form.serviceStart) {
message.error('请选择服务开始时间')
return
}
if (!form.serviceEnd) {
message.error('请选择服务结束时间')
return
}
if (!form.activityCategoryId) {
message.error('请选择活动类型')
return
}
if (!form.activityNameId) {
message.error('请选择活动名称')
return
}
if (!form.serviceContent) {
message.error('请输入服务内容描述')
return
}
if (uploadedFiles.value.length === 0) {
message.error('请上传证明材料')
return
}
loading.value = true
try {
3 months ago
// 1. 直接使用已上传文件的ID集合(文件上传时已经调用过接口)
const fileIds = uploadedFiles.value.map(file => file.id).filter(id => id)
console.log('提交申报,文件ID集合:', fileIds)
3 months ago
// 2. 将文件ID作为附件ID传递给申报接口
const submitData = {
...form,
attachmentIds: fileIds.join(','), // 将文件ID列表转换为逗号分隔的字符串
status: 1 // 提交状态
}
3 months ago
// 确保必要的ID字段存在(与PC端逻辑一致)
if (!submitData.userId) {
submitData.userId = form.userId
}
if (!submitData.firmId) {
submitData.firmId = form.firmId
}
console.log('提交数据:', submitData)
3 months ago
// 3. 调用提交接口(使用正确的API变量)
const res = await serviceApplicationsApi.addSubmit(submitData)
if (res.code === 0) {
message.success('申报提交成功')
setTimeout(() => {
router.push('/mobile/service')
}, 1000)
} else {
message.error(res.msg || '提交失败')
}
} catch (error) {
console.error('提交失败:', error)
3 months ago
message.error(error.message || '提交失败,请稍后重试')
} finally {
loading.value = false
}
}
// 返回
function handleBack() {
router.back()
}
onMounted(() => {
getUserInfo()
getSelectOptions()
3 months ago
// 检查是否有applicationId参数(编辑草稿模式)
const applicationId = route.query.applicationId
if (applicationId) {
console.log('编辑草稿模式,applicationId:', applicationId)
loadDraftData(applicationId)
}
})
3 months ago
// 加载草稿数据
async function loadDraftData(applicationId) {
loading.value = true
try {
const res = await serviceApplicationsApi.queryDetail(applicationId)
if (res.code === 0) {
// 将草稿数据合并到表单中
Object.assign(form, res.data)
console.log('草稿数据加载成功:', res.data)
// 如果有附件ID,则加载文件列表
if (res.data.attachmentIds) {
await getFileListByAttachmentIds(res.data.attachmentIds)
}
} else {
message.error(res.msg || '加载草稿数据失败')
}
} catch (error) {
console.error('加载草稿数据失败:', error)
message.error('网络错误,请稍后重试')
} finally {
loading.value = false
}
}
// 根据attachmentIds获取文件列表
async function getFileListByAttachmentIds(attachmentIds) {
try {
if (!attachmentIds || !attachmentIds.trim()) {
uploadedFiles.value = []
return
}
console.log('开始获取文件列表,attachmentIds:', attachmentIds)
// 直接传递attachmentIds字符串,后端接口接收字符串参数
const result = await fileApi.getFileList(attachmentIds)
console.log('文件列表API返回结果:', result)
if (result.code === 0 && result.data && result.data.length > 0) {
// 将文件信息转换为移动端需要的格式
uploadedFiles.value = result.data.map(file => ({
id: file.id,
name: file.fileName || file.name || '未命名文件',
fileName: file.fileName || file.name || '未命名文件',
fileUrl: file.fileUrl,
fileSize: file.fileSize,
uploadTime: file.uploadTime
}))
console.log('文件列表加载成功,文件数量:', uploadedFiles.value.length)
console.log('文件详情:', uploadedFiles.value)
} else {
uploadedFiles.value = []
console.log('文件列表为空或获取失败')
}
} catch (error) {
console.error('获取文件列表失败:', error)
uploadedFiles.value = []
message.error('获取文件列表失败')
}
}
</script>
<style scoped>
.mobile-create-page {
min-height: 100vh;
background-color: #f5f5f5;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
/* 必填字段红星样式 */
.form-label.required::after {
content: '*';
color: #ff4d4f;
margin-left: 4px;
}
/* 头部导航 */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 44px;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
color: white;
padding: 0 16px;
position: sticky;
top: 0;
z-index: 100;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.back-btn {
background: none;
border: none;
color: white;
font-size: 20px;
padding: 8px;
border-radius: 50%;
transition: background-color 0.3s;
}
.back-btn:active {
background-color: rgba(255, 255, 255, 0.2);
}
.header-title {
font-size: 17px;
font-weight: 600;
flex: 1;
text-align: center;
}
/* 表单日期选择器样式 */
.form-date-picker {
width: 100% !important;
}
.form-date-picker .ant-picker {
width: 100% !important;
border: 1px solid #d9d9d9 !important;
border-radius: 8px !important;
padding: 12px !important;
font-size: 14px !important;
background: white !important;
transition: all 0.3s !important;
box-sizing: border-box !important;
}
.form-date-picker .ant-picker:hover {
border-color: #40a9ff !important;
}
.form-date-picker .ant-picker:focus,
.form-date-picker .ant-picker-focused {
border-color: #1890ff !important;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2) !important;
}
.form-date-picker .ant-picker-input > input {
font-size: 14px !important;
height: auto !important;
}
.form-date-picker .ant-picker-input > input::placeholder {
color: #bfbfbf !important;
}
/* 禁用状态样式 */
.form-date-picker .ant-picker-disabled {
background-color: #f5f5f5 !important;
color: #999 !important;
cursor: not-allowed !important;
}
/* 表单内容 */
.form-content {
padding: 16px;
padding-bottom: 80px;
}
.form-section {
background: white;
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border: 1px solid #e8e8e8;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #1890ff;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 2px solid #e8f4ff;
position: relative;
}
.section-title::before {
content: '';
position: absolute;
left: 0;
bottom: -2px;
width: 60px;
height: 2px;
background: #1890ff;
border-radius: 1px;
}
.form-item {
margin-bottom: 20px;
}
.form-label {
display: block;
font-size: 14px;
font-weight: 500;
color: #333;
margin-bottom: 6px;
line-height: 1.4;
}
.label-tip {
color: #999;
font-size: 12px;
font-weight: normal;
margin-left: 4px;
}
.form-input, .form-select, .form-textarea {
width: 100%;
padding: 12px;
border: 1px solid #d9d9d9;
border-radius: 8px;
font-size: 14px;
background: white;
transition: all 0.3s;
box-sizing: border-box;
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.form-input:disabled, .form-select:disabled, .form-textarea:disabled {
background-color: #f5f5f5;
color: #999;
cursor: not-allowed;
}
.form-textarea {
resize: vertical;
min-height: 80px;
line-height: 1.5;
}
.form-tip {
font-size: 12px;
color: #ff4d4f;
margin-top: 4px;
line-height: 1.4;
}
/* 文件上传 */
.file-input {
width: 100%;
padding: 12px;
border: 2px dashed #d9d9d9;
border-radius: 8px;
background: #fafafa;
text-align: center;
cursor: pointer;
transition: all 0.3s;
margin-bottom: 12px;
}
.file-input:hover {
border-color: #1890ff;
background: #f0f8ff;
}
.file-input:disabled {
cursor: not-allowed;
opacity: 0.6;
}
.file-list {
margin-top: 8px;
}
.file-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: #f8f9fa;
border-radius: 6px;
margin-bottom: 6px;
border-left: 3px solid #1890ff;
}
.file-name {
font-size: 13px;
color: #333;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.remove-btn {
background: #ff4d4f;
color: white;
border: none;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: background-color 0.3s;
}
.remove-btn:hover:not(:disabled) {
background: #ff7875;
}
.remove-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
/* 操作按钮 */
.action-buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
padding: 12px 16px;
display: flex;
gap: 12px;
border-top: 1px solid #e8e8e8;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
}
.btn {
flex: 1;
padding: 12px 16px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
text-align: center;
}
.btn-primary {
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
color: white;
}
.btn-primary:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.4);
}
.btn-secondary {
background: white;
color: #1890ff;
border: 1px solid #1890ff;
}
.btn-secondary:hover:not(:disabled) {
background: #f0f8ff;
transform: translateY(-1px);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none !important;
box-shadow: none !important;
}
/* 加载状态 */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.loading-spinner {
background: white;
padding: 20px 24px;
border-radius: 8px;
font-size: 14px;
color: #333;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* 响应式调整 */
@media (max-width: 375px) {
.form-content {
padding: 12px;
padding-bottom: 80px;
}
.form-section {
padding: 12px;
}
.action-buttons {
padding: 10px 12px;
}
}
/* 输入框动画效果 */
.form-input, .form-select, .form-textarea {
animation: fadeInUp 0.3s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 4px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 2px;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 2px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
</style>