810 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="system-menu-container layout-padding">
<el-tabs v-model="activeTab" class="tabs" @tab-click="handleClick">
<el-tab-pane label="团队管理" name="first">
<ProTable ref="tableRefteam" :request-api="teamPageGet" :columns="columns" :pagination="true"
:tool-button="true">
<template #tools>
<el-button type="primary" icon="ele-Plus" @click="openTeam()"> 新增团队
</el-button>
</template>
<template #index="scope">
{{ scope.$index + 1 }}
</template>
<template #action="scope">
<el-button icon="ele-Edit" size="small" text type="primary" @click="openTeam(scope.row.id)">
编辑 </el-button>
<el-popconfirm title="确认删除此团队吗?" @confirm="onDeleteTeam(scope.row.id)">
<template #reference>
<el-button icon="ele-Delete" size="small" text type="danger">
删除
</el-button>
</template>
</el-popconfirm>
</template>
</ProTable>
</el-tab-pane>
<el-tab-pane label="运动员管理" name="second">
<ProTable ref="tableRefSport" :request-api="appuserAthletePageGet" :columns="columnsSport"
:pagination="true" :tool-button="true" :initParam="initParam">
<template #tools>
<el-button type="primary" icon="ele-Plus" @click="onOpenMenu()"> 新增
</el-button>
</template>
<template #index="scope">
{{ scope.$index + 1 }}
</template>
<template #idCardImageUrl="scope">
<el-image :src="scope.row.idCardImageUrl" style="width: 100px; " fit="cover" />
</template>
<template #action="scope">
<el-button icon="ele-Edit" size="small" text type="primary" @click="onOpenMenu(scope.row)">
编辑 </el-button>
<el-popconfirm title="确认删除此运动员吗?" @confirm="onDeleteMenu(scope.row.id)">
<template #reference>
<el-button icon="ele-Delete" size="small" text type="danger">
删除
</el-button>
</template>
</el-popconfirm>
</template>
</ProTable>
</el-tab-pane>
</el-tabs>
<!-- 团队新增弹窗 -->
<el-dialog :title="teamFormData.id ? '编辑团队' : '新增团队'" v-model="isShowTeamDialog" width="800px" style="min-width: 600px;">
<el-form ref="teamFormRef" :model="teamFormData" style="min-height: 250px;" :rules="teamFormRules" label-width="120px" v-if="isShowTeamDialog">
<el-form-item label="团队名称" prop="name">
<el-input v-model="teamFormData.name" placeholder="请输入团队名称" />
</el-form-item>
<el-form-item label="队员" prop="memberList">
<div>
<el-button type="primary" @click="openMemberDialog">添加队员</el-button>
<div style="margin-top:8px">
<template v-if="teamFormData.memberList && teamFormData.memberList.length">
<el-tag v-for="m in teamFormData.memberList" :key="m.id" closable @close="() => removeMember(m.id)" style="margin-right:6px">{{ m.name }}</el-tag>
</template>
<template v-else>
<span class="muted">未选择队员</span>
</template>
</div>
</div>
</el-form-item>
<el-form-item label="领队" prop="leaderIds">
<el-select v-model="teamFormData.leaderIds" multiple placeholder="请选择领队">
<el-option v-for="opt in leaderOptions" :key="opt.value" :label="opt.label" :value="opt.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="教练" prop="coachIds">
<el-select v-model="teamFormData.coachIds" multiple placeholder="请选择教练">
<el-option v-for="opt in coachOptions" :key="opt.value" :label="opt.label" :value="opt.value"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="isShowTeamDialog=false">取消</el-button>
<el-button type="primary" @click="onTeamSubmit">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 成员选择弹窗 -->
<el-dialog title="选择队员" v-model="isShowMemberDialog" width="900px">
<ProTable ref="memberTableRef" :request-api="appuserAthletePageGet" :columns="columnsMember" :initParam="initParam" :pagination="true" />
<template #footer>
<span class="dialog-footer">
<el-button @click="cancelMemberSelection">取消</el-button>
<el-button type="primary" @click="confirmMemberSelection">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 弹窗 -->
<el-dialog :title="dialogTitle" v-model="isShowDialog" width="width: 800px;">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="200px" style="margin-bottom: 30px;min-height: 250rpx;"
v-if="isShowDialog">
<!-- 证件类型选择 -->
<el-form-item label="运动员身份证认证">
<el-radio-group v-model="ocrType">
<el-radio label="idcard">身份证认证</el-radio>
<el-radio label="passport">护照认证</el-radio>
</el-radio-group>
</el-form-item>
<!-- 证件照片上传 -->
<el-form-item :label="ocrType === 'passport' ? '护照照片' : '身份证照片'" prop="idCardImageUrl">
<el-upload class="avatar-uploader" action="/api/file/upload" :show-file-list="false"
:on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
<img v-if="formData.idCardImageUrl" :src="formData.idCardImageUrl" class="avatar" style="height: 150px;" />
<el-icon v-else class="avatar-uploader-icon">
<Plus />
</el-icon>
</el-upload>
<div style="color: #909399; font-size: 12px; margin-top: 4px;">
请先选择证件类型,再上传对应的证件照片进行识别
</div>
</el-form-item>
<!-- 运动员姓名 -->
<el-form-item label="姓名" prop="name">
<el-input v-model="formData.name" disabled placeholder="请输入运动员姓名"></el-input>
</el-form-item>
<!-- 性别 -->
<el-form-item label="性别" prop="gender">
<el-tag v-if="formData.gender !== ''" :type="formData.gender === 0 ? '' : 'success'">
{{ formData.gender === 0 ? '男' : '女' }}
</el-tag>
<span v-else style="color: #909399;">请选择性别</span>
</el-form-item>
<el-form-item v-if="ocrType === 'idcard'" label="身份证号" prop="idCardNumber">
<el-input v-model="formData.idCardNumber" disabled placeholder="请输入身份证号"></el-input>
</el-form-item>
<el-form-item v-if="ocrType === 'passport'" label="出生日期" prop="birthDate">
<el-input v-model="formData.birthDate" placeholder="请选择出生日期" disabled></el-input>
</el-form-item>
<!-- 联系电话 -->
<el-form-item label="联系电话" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入联系电话" style="width: 360px;"></el-input>
<el-button
v-if="needVerificationCode"
type="text"
@click="onSendCode"
style="margin-left: 8px;"
:disabled="sending || !isPhoneValid">
{{ sending ? `${sendTimer}s` : '获取验证码' }}
</el-button>
<el-tag v-else-if="formData.mobile && !isMobileChanged" type="info" style="margin-left: 8px;">手机号未变更</el-tag>
</el-form-item>
<!-- 验证码 -->
<el-form-item label="验证码" prop="verificationCode" v-if="needVerificationCode">
<el-input v-model="formData.verificationCode" placeholder="请输入验证码" maxlength="6" style="width: 360px;"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel">取消</el-button>
<el-button type="primary" @click="onSubmit">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup name="sysMenu">
import { defineAsyncComponent, ref, reactive, nextTick, computed } from 'vue';
import { ElMessage, ElForm } from 'element-plus';
import SysMenuApi from '/@/api/SysMenuApi';
import { auths } from '/@/utils/authFunction';
import ProTable from '/@/components/ProTable/index.vue';
import { teamPageGet, teamDetailGet, teamAddPost, teamDeleteDelete, teamUpdatePut } from '/@/api/match/unituser';
import { appuserAthletePageGet, appuserAthleteAddPost, appuserAthleteUpdatePut, appuserLeaderPageGet, appuserCoachPageGet, appuserMemberDeletePost } from '/@/api/match/appuser';
import { commonOcrRecognizeIdcardPost, commonOcrRecognizePassportPost, commonSmsSendVerificationCodePost } from '/@/api/match/common';
// 定义表单引用类型
const formRef = ref();
const activeTab = ref('first')
const getDefaultFormData = () => ({
id: undefined,
name: '',
idCardNumber: '',
gender: '',
idCardImageUrl: '',
birthDate: '',
mobile: '',
verificationCode: '',
});
const formData = ref(getDefaultFormData())
// 证件识别类型:idcard-身份证,passport-护照
const ocrType = ref('idcard')
const initParam = ref({
personType: 1,
refresh: true,
})
const formRules = computed(() => {
// 编辑运动员且原来有手机号,则手机号必填;否则非必填
const isMobileRequired = !!(formData.value.id && originalMobile.value);
const baseRules = {
name: [
{ required: true, message: '请输入运动员姓名', trigger: 'blur' }
],
idCardNumber: [
{ required: true, message: '请输入身份证号', trigger: 'change' }
],
gender: [
{ required: true, message: '请选择性别', trigger: 'change' }
],
mobile: isMobileRequired
? [
{ required: true, message: '请输入联系电话', trigger: 'blur' },
{
validator: (rule, value, cb) => {
const reg = /^1[3-9]\d{9}$/;
if (!value) return cb(new Error('请输入联系电话'));
if (!reg.test(value)) return cb(new Error('请输入正确的手机号'));
cb();
},
trigger: 'blur'
}
]
: [
{
validator: (rule, value, cb) => {
if (!value) return cb(); // 非必填,允许为空
const reg = /^1[3-9]\d{9}$/;
if (!reg.test(value)) return cb(new Error('请输入正确的手机号'));
cb();
},
trigger: 'blur'
}
],
idCardImageUrl: [
{ required: true, message: '请上传身份证照片', trigger: 'change' }
],
birthDate: [
{ required: true, message: '请选择出生日期', trigger: 'change' }
],
};
// 填写了手机号且手机号发生变更(新增或修改)时,验证码必填
if (needVerificationCode.value) {
baseRules.verificationCode = [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ len: 6, message: '验证码长度为6位', trigger: 'blur' }
];
}
return baseRules;
});
const columns = ref([
{ prop: 'index', label: '序号', align: 'center', },
{ prop: 'name', label: '团队名称', search: { el: 'input' } },
{ prop: 'leaderNames', label: '领队' },
{ prop: 'coachNames', label: '教练' },
{ prop: 'action', label: '操作', align: 'center', fixed: 'right' },
]);
const columnsSport = ref([
{ prop: 'index', label: '序号', align: 'center', },
{ prop: 'name', label: '运动员姓名', search: { el: 'input' } },
{ prop: 'age', label: '年龄', },
{
prop: 'gender', label: '性别', search: {
el: 'select',
},
enum: [
{ label: '全部', value: '' },
{ label: '男', value: 0 },
{ label: '女', value: 1 },
],
},
// { prop: 'idCardImageUrl', label: '身份证' },
{ prop: 'idCardNumber', label: '身份证号' },
{ prop: 'mobile', label: '联系电话', search: { el: 'input' } },
{ prop: 'action', label: '操作', align: 'center', fixed: 'right' },
]);
const isShowDialog = ref(false);
const dialogTitle = ref('新增');
const tableRefteam = ref(null);
// 验证码相关
const sending = ref(false);
const sendTimer = ref(0);
const sendInterval = ref(null);
const originalMobile = ref('');
const isPhoneValid = computed(() => {
const reg = /^1[3-9]\d{9}$/;
return reg.test(formData.value.mobile);
});
const isMobileChanged = computed(() => {
if (!formData.value.id) return true;
return formData.value.mobile !== originalMobile.value;
});
const needVerificationCode = computed(() => {
// 填写了手机号 且 手机号发生了变更(新增或修改),才需要验证码
return !!(formData.value.mobile && isMobileChanged.value);
});
// 团队新增弹窗相关
const isShowTeamDialog = ref(false);
const teamFormRef = ref();
const teamFormData = ref({
id: undefined,
name: '',
memberIds: [],
memberList: [], // [{id,name}]
leaderIds: [],
coachIds: [],
memberNames: '',
});
const validateMemberList = (rule, value, callback) => {
if (!teamFormData.value.memberList || teamFormData.value.memberList.length === 0) {
callback(new Error('请至少选择一名队员'));
} else {
callback();
}
};
const validateLeaderIds = (rule, value, callback) => {
if (!value || value.length === 0) {
callback(new Error('请选择领队'));
} else {
callback();
}
};
const validateCoachIds = (rule, value, callback) => {
if (!value || value.length === 0) {
callback(new Error('请选择教练'));
} else {
callback();
}
};
const teamFormRules = ref({
name: [{ required: true, message: '请输入团队名称', trigger: 'blur' }],
memberList: [{ required: true, validator: validateMemberList, trigger: ['change', 'blur'] }],
leaderIds: [{ required: true, validator: validateLeaderIds, trigger: ['change', 'blur'] }],
coachIds: [{ required: true, validator: validateCoachIds, trigger: ['change', 'blur'] }],
});
// 成员选择弹窗(使用 ProTable 的多选能力)
const isShowMemberDialog = ref(false);
const memberTableRef = ref(null);
const columnsMember = ref([
{ type: 'selection' },
{ prop: 'name', label: '姓名', align: 'center' },
{ prop: 'idCardNumber', label: '身份证号', align: 'center' },
{ prop: 'mobile', label: '联系电话', align: 'center' },
]);
// 领队/教练下拉选项
const leaderOptions = ref([]);
const coachOptions = ref([]);
const loadLeaderOptions = async () => {
try {
const res = await appuserLeaderPageGet({ PageNo: 1, PageSize: 100 , personType:3});
if (res && res.succeeded && res.data && Array.isArray(res.data.rows)) {
leaderOptions.value = res.data.rows.map((r) => ({ label: r.name, value: r.id }));
}
} catch (error) {
console.error('加载领队失败', error);
}
};
const loadCoachOptions = async () => {
try {
const res = await appuserCoachPageGet({ PageNo: 1, PageSize: 100, personType:2 });
if (res && res.succeeded && res.data && Array.isArray(res.data.rows)) {
coachOptions.value = res.data.rows.map((r) => ({ label: r.name, value: r.id }));
}
} catch (error) {
console.error('加载教练失败', error);
}
};
// 打开新增团队弹窗
const openTeam = async (id) => {
// reset
teamFormData.value = { id: undefined, name: '', memberIds: [], memberList: [], leaderIds: [], coachIds: [], memberNames: '' };
await Promise.all([loadLeaderOptions(), loadCoachOptions()]);
if (id) {
// 编辑:回显数据
try {
const res = await teamDetailGet(id);
if (res && res.succeeded && res.data) {
const d = res.data;
teamFormData.value.id = d.id;
teamFormData.value.name = d.name || '';
// 处理成员(后端返回 members 数组,包含 userId / name)
if (Array.isArray(d.members)) {
// 根据 personType 区分成员类型:1-运动员,2-教练,3-领队
const athletes = [];
const coachIds = [];
const leaderIds = [];
d.members.forEach((m) => {
const id = m.userId ?? m.id;
const name = m.name ?? m.realName ?? m.fullName ?? '';
const pt = m.personType ?? m.type ?? null;
if (pt === 1 || String(pt) === '1') {
athletes.push({ id, name });
} else if (pt === 2 || String(pt) === '2') {
coachIds.push(id);
} else if (pt === 3 || String(pt) === '3') {
leaderIds.push(id);
} else {
// 如果没有 personType,尝试根据标记字段推断(优先填到运动员)
athletes.push({ id, name });
}
});
teamFormData.value.memberList = athletes;
teamFormData.value.memberIds = athletes.map((m) => m.id);
teamFormData.value.memberNames = athletes.map((m) => m.name).join(',');
// 优先使用 members 中的领队/教练 id,否则回退到返回的 leaderIds/coaches 字段
teamFormData.value.leaderIds = leaderIds.length ? leaderIds : (d.leaderIds ?? d.leaders?.map((l) => l.id) ?? []);
teamFormData.value.coachIds = coachIds.length ? coachIds : (d.coachIds ?? d.coaches?.map((c) => c.id) ?? []);
}
}
} catch (error) {
console.error('加载团队详情失败', error);
}
}
isShowTeamDialog.value = true;
};
// 打开成员选择弹窗
const openMemberDialog = () => {
isShowMemberDialog.value = true;
// ProTable 会自动请求数据(requestAuto = true),清空以前选择
// 尝试在表格加载后预选已存在成员
nextTick(async () => {
try {
// 清空已有选择
memberTableRef.value?.clearSelection?.();
// 等待表格数据加载;尝试重试几次以兼容异步加载
const maxRetry = 6;
let attempt = 0;
const trySelect = () => {
const tableDataRef = memberTableRef.value?.tableData;
const tableData = tableDataRef && tableDataRef.value ? tableDataRef.value : tableDataRef || [];
const tableElRef = memberTableRef.value?.element;
const tableEl = tableElRef && tableElRef.value ? tableElRef.value : tableElRef;
if (Array.isArray(tableData) && tableData.length) {
// 对每个已选 memberId,在表格当前页中找到对应行并选中
teamFormData.value.memberIds.forEach((id) => {
const row = tableData.find((r) => r.id === id);
if (row && tableEl && typeof tableEl.toggleRowSelection === 'function') {
tableEl.toggleRowSelection(row, true);
}
});
return true;
}
return false;
};
while (attempt < maxRetry) {
if (trySelect()) break;
attempt++;
// 等待 200ms 再试
// eslint-disable-next-line no-await-in-loop
await new Promise((r) => setTimeout(r, 200));
}
} catch (e) {
console.error('预选成员失败', e);
}
});
};
const confirmMemberSelection = () => {
const selected = memberTableRef.value?.selectedList || [];
// 保持 memberList 为对象数组,方便删除
teamFormData.value.memberList = selected.map((s) => ({ id: s.id, name: s.name }));
teamFormData.value.memberIds = teamFormData.value.memberList.map((m) => m.id);
teamFormData.value.memberNames = teamFormData.value.memberList.map((m) => m.name).join(',');
isShowMemberDialog.value = false;
};
const removeMember = (id) => {
teamFormData.value.memberList = teamFormData.value.memberList.filter((m) => m.id !== id);
teamFormData.value.memberIds = teamFormData.value.memberList.map((m) => m.id);
teamFormData.value.memberNames = teamFormData.value.memberList.map((m) => m.name).join(',');
};
const cancelMemberSelection = () => {
isShowMemberDialog.value = false;
};
// 提交新增团队
const onTeamSubmit = () => {
if (!teamFormRef.value) return;
teamFormRef.value.validate(async (valid) => {
if (!valid) return;
try {
const payload = {
name: teamFormData.value.name,
memberIds: teamFormData.value.memberIds,
leaderIds: teamFormData.value.leaderIds,
coachIds: teamFormData.value.coachIds,
};
const res = teamFormData.value.id ? await teamUpdatePut({ id: teamFormData.value.id, ...payload }) : await teamAddPost(payload);
if (res && res.succeeded) {
ElMessage.success(teamFormData.value.id ? '更新团队成功' : '新增团队成功');
isShowTeamDialog.value = false;
// 刷新团队表格(ProTable 暴露的 getTableList)
try {
await nextTick();
tableRefteam.value?.getTableList?.();
} catch (e) {
// ignore
}
// 切回团队页签
activeTab.value = 'first';
} else {
ElMessage.error(res.errors || res.msg || (teamFormData.value.id ? '更新团队失败' : '新增团队失败'));
}
} catch (error) {
ElMessage.error(teamFormData.value.id ? '更新团队失败' : '新增团队失败');
console.error(error);
}
});
};
const onOpenMenu = (row) => {
// 重置验证码相关状态
if (sendInterval.value) {
clearInterval(sendInterval.value);
sendInterval.value = null;
}
sending.value = false;
sendTimer.value = 0;
dialogTitle.value = row ? '编辑' : '新增';
formData.value = getDefaultFormData();
ocrType.value = 'idcard';
// 保存原始手机号
originalMobile.value = '';
if (row && typeof row === 'object') {
formData.value = {
...getDefaultFormData(),
id: row.id,
name: row.name ?? '',
idCardNumber: row.idCardNumber ?? '',
gender: row.gender !== undefined && row.gender !== null ? Number(row.gender) : '',
idCardImageUrl: row.idCardImageUrl ?? '',
birthDate: row.birthDate ?? '',
mobile: row.mobile ?? row.mobile ?? '',
};
originalMobile.value = row.mobile ?? row.mobile ?? '';
}
isShowDialog.value = true;
};
//删除运动员
const onDeleteMenu = async (id) => {
try {
const res = await appuserMemberDeletePost(id);
if (res && res.succeeded) {
ElMessage.success('删除成功');
// 刷新运动员表格
try {
await nextTick();
initParam.value.refresh = !initParam.value.refresh;
} catch (e) {
// ignore
}
} else {
ElMessage.error(res.errors || res.msg || '删除失败');
}
} catch (e) {
ElMessage.error('删除失败');
console.error(e);
}
};
// 删除团队(使用正确的 team API)
const onDeleteTeam = async (id) => {
try {
const res = await teamDeleteDelete({ id });
if (res && res.succeeded) {
ElMessage.success('删除团队成功');
// 刷新团队表格
try {
await nextTick();
tableRefteam.value?.getTableList?.();
} catch (e) {
// ignore
}
} else {
ElMessage.error(res.errors || res.msg || '删除团队失败');
}
} catch (e) {
ElMessage.error('删除团队失败');
console.error(e);
}
};
const onSendCode = async () => {
if (!isPhoneValid.value) {
ElMessage.error('请输入正确的手机号');
return;
}
if (!isMobileChanged.value) {
ElMessage.warning('手机号未变更,无需获取验证码');
return;
}
sending.value = true;
sendTimer.value = 60;
try {
const res = await commonSmsSendVerificationCodePost({ phone: formData.value.mobile });
if (res.succeeded) {
ElMessage.success('验证码已发送');
sendInterval.value = setInterval(() => {
sendTimer.value -= 1;
if (sendTimer.value <= 0) {
clearInterval(sendInterval.value);
sending.value = false;
}
}, 1000);
} else {
ElMessage.error(res.errors || res.msg || '发送验证码失败');
sending.value = false;
}
} catch (err) {
sending.value = false;
ElMessage.error(err && err.message ? err.message : '发送验证码失败');
}
};
const handleAvatarSuccess = async (response,
uploadFile) => {
//response[0].url
const imageUrl = response[0].url;
const res = ocrType.value === 'passport'
? await commonOcrRecognizePassportPost({ imageUrl })
: await commonOcrRecognizeIdcardPost({ imageUrl });
if(res.succeeded){
if(ocrType.value === 'passport'){
formData.value.name = res.data.name
formData.value.idCardNumber = res.data.passportNumber
formData.value.gender = res.data.gender == 'Male' ? 0 : 1
formData.value.idCardImageUrl = imageUrl
formData.value.birthDate = res.data.issueDateYmd
}else{
formData.value.name = res.data.name
formData.value.idCardNumber = res.data.idCardNumber
formData.value.gender = res.data.gender == '男' ? 0 : 1
formData.value.idCardImageUrl = imageUrl
formData.value.birthDate = res.data.birthDate
}
}else{
ElMessage.error('请上传正确的身份证/护照')
}
console.log(res)
}
const beforeAvatarUpload = () => {
// if (rawFile.type !== 'image/jpeg') {
// ElMessage.error('Avatar picture must be JPG format!')
// return false
// } else if (rawFile.size / 1024 / 1024 > 2) {
// ElMessage.error('Avatar picture size can not exceed 2MB!')
// return false
// }
return true
}
// 表单提交
const onSubmit = () => {
if (!formRef.value) return;
formRef.value.validate(async (valid) => {
if (!valid) return;
try {
const payload = {
...formData.value,
gender: formData.value.gender !== undefined && formData.value.gender !== null ? Number(formData.value.gender) : '',
mobile: formData.value.mobile,
};
const res = formData.value.id ? await appuserAthleteUpdatePut(payload) : await appuserAthleteAddPost(payload);
if (res.succeeded) {
ElMessage.success(dialogTitle.value + '成功');
isShowDialog.value = false;
initParam.value.refresh = !initParam.value.refresh;
// 清理计时器
if (sendInterval.value) {
clearInterval(sendInterval.value);
sendInterval.value = null;
}
sending.value = false;
sendTimer.value = 0;
} else {
ElMessage.error(res.errors || res.msg || dialogTitle.value + '失败');
}
} catch (error) {
ElMessage.error(dialogTitle.value + '失败');
console.error(error);
}
});
};
// 取消
const onCancel = () => {
// 清理计时器
if (sendInterval.value) {
clearInterval(sendInterval.value);
sendInterval.value = null;
}
sending.value = false;
sendTimer.value = 0;
isShowDialog.value = false;
};
</script>
<style scoped lang="scss">
.tabs {
height: 100%;
}
.dialog-footer {
margin-top: 80px;
}
/* 时长组合框样式 */
.duration-row {
display: flex;
align-items: center;
gap: 8px;
}
.duration-row .el-input {
width: 120px;
}
.duration-separator {
color: #606266;
font-size: 14px;
}
.duration-label {
color: #909399;
font-size: 14px;
}
</style>
<style>
.avatar-uploader .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
</style>
<style>
#pane-first {
height: 100%;
}
#pane-second {
height: 100%;
}
</style>