新增到课签到时间窗口与迟到签到时间窗口配置,优化教练评分机制(未测试)
This commit is contained in:
@@ -118,22 +118,22 @@ WHERE NOT EXISTS (SELECT 1 FROM course_type_label WHERE type_id = 103 AND label_
|
||||
|
||||
-- 晨间瑜伽 | A教室 | 明天 08:00-09:00 | 教练100
|
||||
INSERT INTO group_course (id, course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, stored_value_amount, create_by, update_by, created_at, updated_at)
|
||||
SELECT 100, '晨间瑜伽', 100, 100, '2026-07-23 08:00:00'::TIMESTAMP, '2026-07-23 09:00:00'::TIMESTAMP, 20, 5, '0', 'A教室', '/static/course/e2e_yoga.jpg', 'E2E测试-晨间瑜伽课程,温和唤醒身体', 88.00, 'system', 'system', NOW(), NOW()
|
||||
SELECT 100, '晨间瑜伽', 100, 100, '2026-07-27 08:00:00'::TIMESTAMP, '2026-07-27 09:00:00'::TIMESTAMP, 20, 5, '0', 'A教室', '/static/course/e2e_yoga.jpg', 'E2E测试-晨间瑜伽课程,温和唤醒身体', 88.00, 'system', 'system', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM group_course WHERE id = 100);
|
||||
|
||||
-- 动感燃脂 | 单车房 | 后天 19:00-20:00 | 教练101
|
||||
INSERT INTO group_course (id, course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, stored_value_amount, create_by, update_by, created_at, updated_at)
|
||||
SELECT 101, '动感燃脂', 101, 101, '2026-07-24 19:00:00'::TIMESTAMP, '2026-07-24 20:00:00'::TIMESTAMP, 25, 10, '0', '单车房', '/static/course/e2e_spin.jpg', 'E2E测试-动感单车燃脂课程', 68.00, 'system', 'system', NOW(), NOW()
|
||||
SELECT 101, '动感燃脂', 101, 101, '2026-07-28 19:00:00'::TIMESTAMP, '2026-07-28 20:00:00'::TIMESTAMP, 25, 10, '0', '单车房', '/static/course/e2e_spin.jpg', 'E2E测试-动感单车燃脂课程', 68.00, 'system', 'system', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM group_course WHERE id = 101);
|
||||
|
||||
-- 有氧搏击 | B教室 | 大后天 10:00-11:00 | 教练100
|
||||
INSERT INTO group_course (id, course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, stored_value_amount, create_by, update_by, created_at, updated_at)
|
||||
SELECT 102, '有氧搏击', 100, 102, '2026-07-25 10:00:00'::TIMESTAMP, '2026-07-25 11:00:00'::TIMESTAMP, 20, 8, '0', 'B教室', '/static/course/e2e_boxing.jpg', 'E2E测试-有氧搏击操课程', 68.00, 'system', 'system', NOW(), NOW()
|
||||
SELECT 102, '有氧搏击', 100, 102, '2026-07-29 10:00:00'::TIMESTAMP, '2026-07-29 11:00:00'::TIMESTAMP, 20, 8, '0', 'B教室', '/static/course/e2e_boxing.jpg', 'E2E测试-有氧搏击操课程', 68.00, 'system', 'system', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM group_course WHERE id = 102);
|
||||
|
||||
-- 普拉提入门 | 瑜伽房 | 4天后 14:00-15:00 | 教练101
|
||||
INSERT INTO group_course (id, course_name, coach_id, course_type, start_time, end_time, max_members, current_members, status, location, cover_image, description, stored_value_amount, create_by, update_by, created_at, updated_at)
|
||||
SELECT 103, '普拉提入门', 101, 103, '2026-07-26 14:00:00'::TIMESTAMP, '2026-07-26 15:00:00'::TIMESTAMP, 15, 3, '0', '瑜伽房', '/static/course/e2e_pilates.jpg', 'E2E测试-普拉提入门课程,适合初学者', 108.00, 'system', 'system', NOW(), NOW()
|
||||
SELECT 103, '普拉提入门', 101, 103, '2026-07-30 14:00:00'::TIMESTAMP, '2026-07-30 15:00:00'::TIMESTAMP, 15, 3, '0', '瑜伽房', '/static/course/e2e_pilates.jpg', 'E2E测试-普拉提入门课程,适合初学者', 108.00, 'system', 'system', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM group_course WHERE id = 103);
|
||||
|
||||
-- ============================================
|
||||
@@ -158,9 +158,9 @@ ON CONFLICT (member_no) DO UPDATE SET
|
||||
|
||||
INSERT INTO sign_in_record (id, member_id, sign_in_time, sign_in_type, sign_in_status, source, created_at, updated_at)
|
||||
VALUES
|
||||
(100, 100, '2026-07-22 08:30:00'::TIMESTAMP, 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', NOW(), NOW()),
|
||||
(101, 101, '2026-07-22 09:00:00'::TIMESTAMP, 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', NOW(), NOW()),
|
||||
(102, 100, '2026-07-22 18:00:00'::TIMESTAMP, 'MANUAL', 'SUCCESS', 'PC_BACKEND', NOW(), NOW())
|
||||
(100, 100, '2026-07-26 08:30:00'::TIMESTAMP, 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', NOW(), NOW()),
|
||||
(101, 101, '2026-07-26 09:00:00'::TIMESTAMP, 'QR_CODE', 'SUCCESS', 'MINI_PROGRAM', NOW(), NOW()),
|
||||
(102, 100, '2026-07-26 18:00:00'::TIMESTAMP, 'MANUAL', 'SUCCESS', 'PC_BACKEND', NOW(), NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- ============================================
|
||||
|
||||
@@ -42,6 +42,21 @@ export interface ViolationRecord {
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface CoachTimeRule {
|
||||
id?: number
|
||||
minDuration?: number | null
|
||||
maxDuration?: number | null
|
||||
normalWindow: number
|
||||
lateWindow: number
|
||||
endGrace: number
|
||||
isDefault?: boolean
|
||||
sortOrder?: number
|
||||
status?: string
|
||||
remark?: string
|
||||
createdAt?: string
|
||||
updatedAt?: string
|
||||
}
|
||||
|
||||
export const coachApi = {
|
||||
/** 获取所有教练列表 */
|
||||
getAll: () =>
|
||||
@@ -71,3 +86,25 @@ export const coachApi = {
|
||||
getViolations: (id: string) =>
|
||||
request.get<ViolationRecord[]>(`/coach/${id}/violations`),
|
||||
}
|
||||
|
||||
export const coachTimeRuleApi = {
|
||||
/** 获取所有时间规则 */
|
||||
getAll: () =>
|
||||
request.get<CoachTimeRule[]>('/coach/time-rules'),
|
||||
|
||||
/** 获取单条规则 */
|
||||
getById: (id: number) =>
|
||||
request.get<CoachTimeRule>(`/coach/time-rules/${id}`),
|
||||
|
||||
/** 创建规则 */
|
||||
create: (data: CoachTimeRule) =>
|
||||
request.post<{ message: string; id: number }>('/coach/time-rules', data),
|
||||
|
||||
/** 更新规则 */
|
||||
update: (id: number, data: CoachTimeRule) =>
|
||||
request.put<{ message: string }>(`/coach/time-rules/${id}`, data),
|
||||
|
||||
/** 删除规则 */
|
||||
delete: (id: number) =>
|
||||
request.delete<{ message: string }>(`/coach/time-rules/${id}`),
|
||||
}
|
||||
|
||||
@@ -46,6 +46,12 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () => import('@/views/system/CoachManagement.vue'),
|
||||
meta: { title: '教练管理' }
|
||||
},
|
||||
{
|
||||
path: 'coach/time-rules',
|
||||
name: 'CoachTimeRuleManagement',
|
||||
component: () => import('@/views/coach/CoachTimeRuleManagement.vue'),
|
||||
meta: { title: '时间规则配置' }
|
||||
},
|
||||
{
|
||||
path: 'roles',
|
||||
name: 'RoleManagement',
|
||||
|
||||
@@ -48,6 +48,7 @@ function transformMenuData(backendMenus: BackendMenuItem[]): MenuItem[] {
|
||||
'statistics/dashboard/index': '/statistics',
|
||||
'banner/index': '/banners',
|
||||
'brand/index': '/brand',
|
||||
'coach/time-rule/index': '/coach/time-rules',
|
||||
}
|
||||
|
||||
const filteredMenus = backendMenus.filter(menu => menu.menuType !== 'F')
|
||||
@@ -113,6 +114,7 @@ function getMenuIcon(menuName: string): string {
|
||||
'数据统计': 'DataAnalysis',
|
||||
'轮播图管理': 'Picture',
|
||||
'品牌定制': 'Brush',
|
||||
'时间规则配置': 'Timer',
|
||||
}
|
||||
return iconMap[menuName] || 'Document'
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ const permissionMapping: PermissionMapping = {
|
||||
'POST /brand': 'brand:config:list',
|
||||
'PUT /brand': 'brand:config:list',
|
||||
'DELETE /brand': 'brand:config:list',
|
||||
'GET /coach/time-rules': 'system:coach:timeRule:list',
|
||||
'POST /coach/time-rules': 'system:coach:timeRule:add',
|
||||
'PUT /coach/time-rules': 'system:coach:timeRule:edit',
|
||||
'DELETE /coach/time-rules': 'system:coach:timeRule:remove',
|
||||
}
|
||||
|
||||
export function checkApiPermission(method: string, url: string): boolean {
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
<template>
|
||||
<div class="time-rule-management">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-title">
|
||||
<span>教练时间规则配置</span>
|
||||
<el-button type="primary" @click="handleAdd">
|
||||
新增规则
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-alert
|
||||
type="info"
|
||||
:closable="false"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<template #title>
|
||||
规则匹配逻辑:系统根据课程时长(分钟)自动匹配区间最精确的规则。若课程时长在 [时长下限, 时长上限] 范围内,则应用该规则的阈值。默认规则(兜底规则)不能设置时长区间,且全局仅允许一个默认规则,设为默认时将自动取消其他默认规则。未匹配到任何规则时,使用默认规则或系统硬编码兜底值。
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="dataSource"
|
||||
style="width: 100%"
|
||||
empty-text="暂无规则配置,将使用系统默认值"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="60" />
|
||||
<el-table-column label="时长下限(分)" width="110">
|
||||
<template #default="{ row }">
|
||||
{{ row.minDuration ?? '无限制' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="时长上限(分)" width="110">
|
||||
<template #default="{ row }">
|
||||
{{ row.maxDuration ?? '无限制' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="正常开课窗口(分)" width="140">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="success" effect="plain">{{ row.normalWindow }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="迟到/缺席窗口(分)" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="warning" effect="plain">{{ row.lateWindow }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结课宽限期(分)" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info" effect="plain">{{ row.endGrace }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="默认" width="70">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.isDefault" type="primary" size="small">默认</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="70">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="row.status === '1' ? 'success' : 'danger'"
|
||||
effect="dark"
|
||||
>
|
||||
{{ row.status === '1' ? '启用' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="160" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link size="small" @click="handleEdit(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" link size="small" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog
|
||||
v-model="modalVisible"
|
||||
:title="modalTitle"
|
||||
width="550px"
|
||||
@closed="resetForm"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:rules="rules"
|
||||
label-width="140px"
|
||||
>
|
||||
<el-form-item label="时长下限(分钟)" prop="minDuration">
|
||||
<el-input-number
|
||||
v-model="formState.minDuration"
|
||||
:min="1"
|
||||
:max="1440"
|
||||
:disabled="formState.isDefault"
|
||||
placeholder="留空表示无下限"
|
||||
style="width: 100%"
|
||||
controls-position="right"
|
||||
/>
|
||||
<div class="form-tip">{{ formState.isDefault ? '默认规则为兜底规则,不限制时长区间' : '课程时长 >= 此值时才应用本规则' }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="时长上限(分钟)" prop="maxDuration">
|
||||
<el-input-number
|
||||
v-model="formState.maxDuration"
|
||||
:min="1"
|
||||
:max="1440"
|
||||
:disabled="formState.isDefault"
|
||||
placeholder="留空表示无上限"
|
||||
style="width: 100%"
|
||||
controls-position="right"
|
||||
/>
|
||||
<div class="form-tip">{{ formState.isDefault ? '默认规则为兜底规则,不限制时长区间' : '课程时长 <= 此值时才应用本规则' }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="正常开课窗口(分钟)" prop="normalWindow">
|
||||
<el-input-number
|
||||
v-model="formState.normalWindow"
|
||||
:min="1"
|
||||
:max="1440"
|
||||
style="width: 100%"
|
||||
controls-position="right"
|
||||
/>
|
||||
<div class="form-tip">开课时间后此分钟内开课视为正常</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="迟到/缺席窗口(分钟)" prop="lateWindow">
|
||||
<el-input-number
|
||||
v-model="formState.lateWindow"
|
||||
:min="1"
|
||||
:max="1440"
|
||||
style="width: 100%"
|
||||
controls-position="right"
|
||||
/>
|
||||
<div class="form-tip">超过正常窗口但在此时间内视为迟到;超过此值视为缺席</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="结课宽限期(分钟)" prop="endGrace">
|
||||
<el-input-number
|
||||
v-model="formState.endGrace"
|
||||
:min="0"
|
||||
:max="1440"
|
||||
style="width: 100%"
|
||||
controls-position="right"
|
||||
/>
|
||||
<div class="form-tip">课程结束后此分钟内允许手动结课,超时自动结课</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="设为默认规则">
|
||||
<el-switch
|
||||
v-model="formState.isDefault"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="onDefaultChange"
|
||||
/>
|
||||
<div class="form-tip form-tip-warn">
|
||||
设为此规则的默认规则前,旧默认规则会自动取消。默认规则不能设置时长区间。
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="formState.status">
|
||||
<el-radio value="1">启用</el-radio>
|
||||
<el-radio value="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
v-model="formState.remark"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="规则说明(选填)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="modalVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleModalOk">
|
||||
确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { coachTimeRuleApi, type CoachTimeRule } from '@/api/coach.api'
|
||||
|
||||
const loading = ref(false)
|
||||
const submitting = ref(false)
|
||||
const dataSource = ref<CoachTimeRule[]>([])
|
||||
const modalVisible = ref(false)
|
||||
const modalTitle = ref('')
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const defaultForm: CoachTimeRule = {
|
||||
minDuration: undefined,
|
||||
maxDuration: undefined,
|
||||
normalWindow: 10,
|
||||
lateWindow: 30,
|
||||
endGrace: 10,
|
||||
isDefault: false,
|
||||
status: '1',
|
||||
remark: '',
|
||||
}
|
||||
|
||||
const formState = reactive<CoachTimeRule>({ ...defaultForm })
|
||||
|
||||
const validateWindowOrder = (_rule: any, _value: any, callback: any) => {
|
||||
if (formState.normalWindow != null && formState.lateWindow != null) {
|
||||
if (formState.lateWindow < formState.normalWindow) {
|
||||
callback(new Error('迟到窗口必须 >= 正常开课窗口'))
|
||||
return
|
||||
}
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
const validateDurationRange = (_rule: any, _value: any, callback: any) => {
|
||||
if (formState.minDuration != null && formState.maxDuration != null) {
|
||||
if (formState.maxDuration < formState.minDuration) {
|
||||
callback(new Error('时长上限必须 >= 时长下限'))
|
||||
return
|
||||
}
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
const rules: FormRules = {
|
||||
normalWindow: [
|
||||
{ required: true, message: '请输入正常开课窗口', trigger: 'blur' },
|
||||
{ validator: validateWindowOrder, trigger: 'change' },
|
||||
],
|
||||
lateWindow: [
|
||||
{ required: true, message: '请输入迟到/缺席窗口', trigger: 'blur' },
|
||||
{ validator: validateWindowOrder, trigger: 'change' },
|
||||
],
|
||||
endGrace: [
|
||||
{ required: true, message: '请输入结课宽限期', trigger: 'blur' },
|
||||
],
|
||||
minDuration: [
|
||||
{ validator: validateDurationRange, trigger: 'change' },
|
||||
],
|
||||
maxDuration: [
|
||||
{ validator: validateDurationRange, trigger: 'change' },
|
||||
],
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
Object.assign(formState, { ...defaultForm, id: undefined })
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
dataSource.value = await coachTimeRuleApi.getAll()
|
||||
} catch {
|
||||
ElMessage.error('获取规则列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
modalTitle.value = '新增规则'
|
||||
resetForm()
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleEdit = (row: CoachTimeRule) => {
|
||||
modalTitle.value = '编辑规则'
|
||||
Object.assign(formState, {
|
||||
id: row.id,
|
||||
minDuration: row.minDuration ?? undefined,
|
||||
maxDuration: row.maxDuration ?? undefined,
|
||||
normalWindow: row.normalWindow,
|
||||
lateWindow: row.lateWindow,
|
||||
endGrace: row.endGrace,
|
||||
isDefault: row.isDefault ?? false,
|
||||
status: row.status ?? '1',
|
||||
remark: row.remark ?? '',
|
||||
})
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const onDefaultChange = (val: boolean) => {
|
||||
if (val) {
|
||||
formState.minDuration = undefined
|
||||
formState.maxDuration = undefined
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (row: CoachTimeRule) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除该规则吗?删除后立即生效。', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
await coachTimeRuleApi.delete(row.id!)
|
||||
ElMessage.success('删除成功,已立即生效')
|
||||
fetchData()
|
||||
} catch {
|
||||
// 用户取消
|
||||
}
|
||||
}
|
||||
|
||||
const handleModalOk = async () => {
|
||||
const valid = await formRef.value?.validate().catch(() => false)
|
||||
if (!valid) return
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
const payload: CoachTimeRule = {
|
||||
minDuration: formState.minDuration ?? null,
|
||||
maxDuration: formState.maxDuration ?? null,
|
||||
normalWindow: formState.normalWindow,
|
||||
lateWindow: formState.lateWindow,
|
||||
endGrace: formState.endGrace,
|
||||
isDefault: formState.isDefault,
|
||||
sortOrder: 0,
|
||||
status: formState.status,
|
||||
remark: formState.remark,
|
||||
}
|
||||
|
||||
if (formState.id) {
|
||||
await coachTimeRuleApi.update(formState.id, payload)
|
||||
ElMessage.success('更新成功,已立即生效')
|
||||
} else {
|
||||
await coachTimeRuleApi.create(payload)
|
||||
ElMessage.success('创建成功,已立即生效')
|
||||
}
|
||||
modalVisible.value = false
|
||||
fetchData()
|
||||
} catch (err: any) {
|
||||
const msg = err?.response?.data?.error || '操作失败'
|
||||
ElMessage.error(msg)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => fetchData())
|
||||
</script>
|
||||
|
||||
<style scoped lang="css">
|
||||
.time-rule-management .card-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.form-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.form-tip-warn {
|
||||
color: #e6a23c;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user