提交 c9c33644 authored 作者: kxjia's avatar kxjia

Tb11.vue

上级 723b3f68
<template> <template>
<div class="bank-report-table" style="height:80vh;"> <div class="bank-report-table" style="height:80vh;" @click="closeAllTooltips">
<vxe-toolbar> <vxe-toolbar>
<template #buttons> <template #buttons>
<div style="margin:10px"> <div style="margin:10px">
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
</div> </div>
</template> </template>
<template #tools> <template #tools>
<!-- <vxe-button status="primary" icon="vxe-icon-edit" @click="checkData()">校验</vxe-button> -->
<vxe-button status="primary" icon="vxe-icon-save" @click="saveBatch()">保存</vxe-button> <vxe-button status="primary" icon="vxe-icon-save" @click="saveBatch()">保存</vxe-button>
</template> </template>
</vxe-toolbar> </vxe-toolbar>
...@@ -54,14 +55,80 @@ ...@@ -54,14 +55,80 @@
<vxe-radio label="否" content="否"></vxe-radio> <vxe-radio label="否" content="否"></vxe-radio>
</vxe-radio-group> </vxe-radio-group>
</template> </template>
<vxe-input <template v-else-if="item.type === 'textarea'">
v-else <vxe-textarea
:type="item.type" v-model="formData[row.code +'_'+ item.field]"
v-model="formData[row.code +'_'+ item.field]" size="mini"
size="mini" class="table-textarea"
class="table-input" :rows="2"
> :autosize="{ minRows: 2, maxRows: 4 }"
</vxe-input> />
</template>
<template v-else>
<div class="input-wrapper" v-if="item.hasRight !== false">
<vxe-input
v-if="item.type !== 'checkbox'"
:type="item.type === 'date' ? 'date' : 'text'"
v-model="formData[row.code +'_'+ item.field]"
size="mini"
class="table-input"
:disabled="!item.hasRight"
@blur="handleInputBlur(row.code, item.field, item.matchedFormula?.formula)"
/>
<vxe-checkbox-group
v-else-if="item.type === 'checkbox'"
v-model="formData[row.code +'_'+ item.field]"
>
<vxe-checkbox
v-for="option in item.options || []"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</vxe-checkbox-group>
<span
v-if="showHelpIcon(row.code, item.field, item)"
class="help-icon"
@click.stop="toggleTooltip(row.code, item.field)"
title="点击查看校验规则"
>
?
</span>
<span
v-if="inputErrors[getFieldKey(row.code, item.field)]"
class="error-icon"
@click.stop="toggleErrorTooltip(row.code, item.field)"
title="点击查看错误详情"
>
<i class="vxe-icon-error"></i>
</span>
<div
v-if="showTooltip && hoveredKey === getFieldKey(row.code, item.field)"
class="tooltip"
@click.stop
>
{{ item.matchedFormula?.des || '暂无校验规则' }}
</div>
<div
v-if="showErrorTooltip && hoveredErrorKey === getFieldKey(row.code, item.field)"
class="error-tooltip"
@click.stop
>
{{ inputErrors[getFieldKey(row.code, item.field)]?.message || '未知错误' }}
<br v-if="inputErrors[getFieldKey(row.code, item.field)]?.formula">
<span v-if="inputErrors[getFieldKey(row.code, item.field)]?.formula" style="color: #ffcccc;">
公式: {{ inputErrors[getFieldKey(row.code, item.field)]?.formula }}
</span>
<template v-if="inputErrors[getFieldKey(row.code, item.field)]?.error">
<br>
<span style="color: #ff9999; font-size: 11px;">
错误: {{ inputErrors[getFieldKey(row.code, item.field)]?.error }}
</span>
</template>
</div>
</div>
<span v-else style="color: #999; font-style: italic;">无权限</span>
</template>
</template> </template>
</template> </template>
</div> </div>
...@@ -69,32 +136,47 @@ ...@@ -69,32 +136,47 @@
</vxe-column> </vxe-column>
<vxe-column field="remarks" title="备注" width="200"> <vxe-column field="remarks" title="备注" width="200">
<template #default="{ row }"> <template #default="{ row }">
<vxe-textarea :rows="row.remarks.rows" v-model="formData[row.code+'_'+row.remarks.field]"> <vxe-textarea
:rows="row.remarks?.rows || 2"
v-model="formData[row.code+'_'+(row.remarks?.field || 'remarks')]"
:disabled="!row.remarks?.hasRight"
>
</vxe-textarea> </vxe-textarea>
</template> </template>
</vxe-column> </vxe-column>
</vxe-table> </vxe-table>
<ValidationDrawer
ref="validationDrawerRef"
v-model="drawerVisible"
:tableFormData="tableFormData"
@validationResultClick="handleValidationResultClick"
/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import AttachTable from '../tableComponents/AttachTable.vue' import AttachTable from '../tableComponents/AttachTable.vue'
import ValidationDrawer from './check/ValidationDrawer.vue'
import { tableFormData } from '../../data/tb11.data'; import { tableFormData } from '../../data/tb11.data';
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted, nextTick, watch } from 'vue'
import { VxeUI } from 'vxe-table' import { VxeUI } from 'vxe-table'
import { batchSaveOrUpdateBeforeDelete, queryRecord } from '../../record/BaosongTaskRecord.api' import { batchSaveOrUpdateBeforeDelete, queryRecord } from '../../record/BaosongTaskRecord.api'
import { allTplItems } from '../../tpl/BaosongTplItem.api' import { allTplItems } from '../../tpl/BaosongTplItem.api'
import { findUserRightForTplItem } from '../../alloc/BaosongTaskAlloc.api'
import { getTblvalidFormula } from '../../tpl/BaosongDataValid.api'
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
const route = useRoute(); const route = useRoute();
const tableRef = ref(); const tableRef = ref();
const tplItemMap = ref({}); const tplItemMap = ref<Record<string, any>>({});
const validationDrawerRef = ref();
const queryParam = ref({ const queryParam = ref({
taskId:-1, taskId: -1,
taskName:'', taskName: '',
tplId:-1, tplId: -1,
tplName:'', tplName: '',
tplCode:'' tplCode: ''
}) })
interface FormData { interface FormData {
...@@ -108,7 +190,32 @@ interface FormData { ...@@ -108,7 +190,32 @@ interface FormData {
rind: number rind: number
} }
onMounted(async ()=>{ interface FormulaItem {
formula: string
des: string
[key: string]: any
}
interface InputError {
message: string
formula: string
error?: string
}
const loading = ref(false)
const formData = reactive<Record<string, any>>({});
const formValues = ref<FormData[]>([])
const userAllocItems = ref<string[]>([])
const validFormula = ref<any[]>([])
const drawerVisible = ref(false)
const showTooltip = ref(false)
const hoveredKey = ref('')
const showErrorTooltip = ref(false)
const hoveredErrorKey = ref('')
const inputErrors = ref<Record<string, InputError>>({})
const isInitialized = ref(false)
onMounted(async () => {
if (route.query.taskId) { if (route.query.taskId) {
queryParam.value.taskId = Number(route.query.taskId); queryParam.value.taskId = Number(route.query.taskId);
} }
...@@ -125,23 +232,69 @@ onMounted(async ()=>{ ...@@ -125,23 +232,69 @@ onMounted(async ()=>{
queryParam.value.tplCode = String(route.query.tplCode); queryParam.value.tplCode = String(route.query.tplCode);
} }
try {
if (queryParam.value.tplId > 0 && queryParam.value.taskId > 0) {
userAllocItems.value = await findUserRightForTplItem({
tplid: queryParam.value.tplId,
taskid: queryParam.value.taskId
})
validFormula.value = await getTblvalidFormula({
tplid: queryParam.value.tplId,
})
}
} catch (error) {
console.error('获取权限或验证公式失败:', error)
VxeUI.modal.message({ content: '获取表单权限或校验规则失败,部分功能可能受限', status: 'warning' })
}
await setTplItemMap() await setTplItemMap()
await setData(); setFormItemRight()
await setData()
isInitialized.value = true
await nextTick()
refreshHelpIcons()
}) })
const loading = ref(false)
const formData = reactive({});
const formValues = ref<FormData[]>([])
const saveBatch = async () => { const saveBatch = async () => {
try { try {
loading.value = true
formValues.value = [] formValues.value = []
// 检查必填项
const requiredErrors: string[] = []
tableFormData.forEach((row: any) => {
if (row.content && Array.isArray(row.content)) {
row.content.forEach((item: any) => {
if (item.field && item.required && item.hasRight !== false) {
const key = getFieldKey(row.code, item.field)
const value = formData[key]
if (!value || (Array.isArray(value) && value.length === 0) || value === '') {
requiredErrors.push(`${row.project || ''} - ${item.label || item.field}`)
}
}
})
}
})
if (requiredErrors.length > 0) {
VxeUI.modal.message({
content: `以下必填项未填写:\n${requiredErrors.join('\n')}`,
status: 'error',
duration: 5000
})
return
}
for (const strKey in formData) { for (const strKey in formData) {
const valData = formData[strKey] const valData = formData[strKey]
if (valData) { if (valData !== undefined && valData !== null && valData !== '') {
let tmpAry = strKey.split("_") const tmpAry = strKey.split("_")
await setFormValues(tmpAry[0],tmpAry[1],valData,1) if (tmpAry.length >= 2) {
await setFormValues(tmpAry[0], tmpAry[1], valData, 1)
}
} }
} }
...@@ -153,14 +306,19 @@ const saveBatch = async () => { ...@@ -153,14 +306,19 @@ const saveBatch = async () => {
} else { } else {
VxeUI.modal.message({ content: '没有需要保存的数据', status: 'warning' }) VxeUI.modal.message({ content: '没有需要保存的数据', status: 'warning' })
} }
} catch (error) { } catch (error: any) {
VxeUI.modal.message({ content: `保存失败: ${error.message}`, status: 'error' }) console.error('保存失败:', error)
VxeUI.modal.message({
content: `保存失败: ${error.message || '未知错误'}`,
status: 'error',
duration: 5000
})
} finally { } finally {
loading.value = false loading.value = false
} }
} }
const childAttachTableRefs = ref({}) const childAttachTableRefs = ref<Record<string, any>>({})
const setAttachTableRef = (el: any, index: string) => { const setAttachTableRef = (el: any, index: string) => {
if (el) { if (el) {
childAttachTableRefs.value[index] = el; childAttachTableRefs.value[index] = el;
...@@ -170,21 +328,36 @@ const setAttachTableRef = (el: any, index: string) => { ...@@ -170,21 +328,36 @@ const setAttachTableRef = (el: any, index: string) => {
const getAttachTableFormData = async () => { const getAttachTableFormData = async () => {
for (const pcode in childAttachTableRefs.value) { for (const pcode in childAttachTableRefs.value) {
const child = childAttachTableRefs.value[pcode]; const child = childAttachTableRefs.value[pcode];
if (child) { if (child && typeof child.getFormData === 'function') {
const datas = child.getFormData() try {
for(const code in datas) { const datas = child.getFormData()
await setFormValues(pcode,code,datas[code],1) if (datas && typeof datas === 'object') {
for(const code in datas) {
await setFormValues(pcode, code, datas[code], 1)
}
}
} catch (error) {
console.error(`获取附件表格 ${pcode} 数据失败:`, error)
} }
} }
} }
}; };
const setFormValues = async (pcode, code, valData, rind) => { const setFormValues = async (pcode: string, code: string, valData: any, rind: number) => {
if(!valData) return; if (valData === undefined || valData === null) return;
let vals = Array.isArray(valData) ? valData.join(',') : valData
let strKey = pcode+"_"+code let vals: string;
if (Array.isArray(valData)) {
vals = valData.filter(item => item !== null && item !== undefined && item !== '').join(',')
} else {
vals = String(valData).trim()
}
if (!vals) return;
const strKey = pcode + "_" + code
const item = tplItemMap.value[strKey]; const item = tplItemMap.value[strKey];
const { pid, itemid } = item ?? {}; const { pid, itemid } = item || {};
let tempForm: FormData = { let tempForm: FormData = {
id: null, id: null,
...@@ -204,42 +377,77 @@ async function setData() { ...@@ -204,42 +377,77 @@ async function setData() {
loading.value = true loading.value = true
const taskId = queryParam.value.taskId const taskId = queryParam.value.taskId
const tplid = queryParam.value.tplId const tplid = queryParam.value.tplId
Object.keys(formData).forEach(key => delete formData[key])
// 清空现有数据
Object.keys(formData).forEach(key => {
delete formData[key]
})
await setTplItemMap(); await setTplItemMap();
if (taskId <= 0 || tplid <= 0) {
console.warn('taskId 或 tplId 无效,跳过数据加载')
return
}
const recordData = await queryRecord({taskid: taskId, tplid: tplid}) const recordData = await queryRecord({taskid: taskId, tplid: tplid})
let valueObj = {} let valueObj: Record<string, string> = {}
for(let data of recordData) {
valueObj[data.itempid+"_" + data.itemid+"_"+ data.rind] = data["content"] if (recordData && Array.isArray(recordData)) {
for(let data of recordData) {
const key = `${data.itempid}_${data.itemid}_${data.rind}`
valueObj[key] = data.content || ''
}
} }
// 加载主表数据
Object.keys(tplItemMap.value).forEach(strKey => { Object.keys(tplItemMap.value).forEach(strKey => {
const item = tplItemMap.value[strKey]; const item = tplItemMap.value[strKey];
const { pid, itemid, formTp } = item ?? {}; const { pid, itemid, formTp } = item || {};
const dataVal = valueObj[pid+"_"+itemid+"_1"] const dataVal = valueObj[`${pid}_${itemid}_1`]
if(formTp == 'checkbox') {
formData[strKey] = dataVal?.split(",") if (dataVal !== undefined) {
} else { if(formTp == 'checkbox') {
formData[strKey] = dataVal formData[strKey] = dataVal ? dataVal.split(",").filter(Boolean) : []
} else {
formData[strKey] = dataVal
}
} }
}); });
// 加载附件表格数据
Object.keys(childAttachTableRefs.value).forEach(pcode => { Object.keys(childAttachTableRefs.value).forEach(pcode => {
const child = childAttachTableRefs.value[pcode]; const child = childAttachTableRefs.value[pcode];
const matchingKeys = Object.keys(tplItemMap.value).filter(key => key.startsWith(pcode + '_')); if (child && typeof child.setFormData === 'function') {
const matchingObjects = matchingKeys.map(key => tplItemMap.value[key]); const matchingKeys = Object.keys(tplItemMap.value).filter(key => key.startsWith(pcode + '_'));
let attachTableData = {} const matchingObjects = matchingKeys.map(key => tplItemMap.value[key]);
for(let tmpData of matchingObjects) { let attachTableData: Record<string, any> = {}
const { pid, itemid, formTp, code } = tmpData ?? {};
if(formTp == 'checkbox') { for(let tmpData of matchingObjects) {
attachTableData[code] = valueObj[pid+"_"+itemid+"_1"]?.split(",") const { pid, itemid, formTp, code } = tmpData || {};
} else { const dataVal = valueObj[`${pid}_${itemid}_1`]
attachTableData[code] = valueObj[pid+"_"+itemid+"_1"]
if (dataVal !== undefined) {
if(formTp == 'checkbox') {
attachTableData[code] = dataVal ? dataVal.split(",").filter(Boolean) : []
} else {
attachTableData[code] = dataVal
}
}
}
if (Object.keys(attachTableData).length > 0) {
child.setFormData(attachTableData)
} }
} }
child.setFormData(attachTableData)
}) })
} catch (error) { } catch (error: any) {
VxeUI.modal.message({ content: `加载数据失败: ${error.message}`, status: 'error' }) console.error('加载数据失败:', error)
VxeUI.modal.message({
content: `加载数据失败: ${error.message || '未知错误'}`,
status: 'error',
duration: 3000
})
} finally { } finally {
loading.value = false loading.value = false
} }
...@@ -248,18 +456,289 @@ async function setData() { ...@@ -248,18 +456,289 @@ async function setData() {
async function setTplItemMap() { async function setTplItemMap() {
try { try {
const tplid = queryParam.value.tplId const tplid = queryParam.value.tplId
if (tplid <= 0) {
console.warn('tplId 无效,跳过加载模板项')
return
}
const tplItem = await allTplItems({ const tplItem = await allTplItems({
tplid: tplid, tplid: tplid,
}) })
tplItemMap.value = {}
tplItem.forEach(item => { tplItem.forEach(item => {
let strKey = item.pcode + "_" + item.xmlcode; let strKey = item.pcode + "_" + item.xmlcode;
tplItemMap.value[strKey] = {pid:item.pid, itemid:item.id, formTp:item.formTp, code:item.xmlcode}; tplItemMap.value[strKey] = {
pid: item.pid,
itemid: item.id,
formTp: item.formTp,
code: item.xmlcode,
label: item.label
};
}) })
} catch (error) {
VxeUI.modal.message({ content: `加载数据失败: ${error.message}`, status: 'error' }) console.log('tplItemMap 加载完成,数量:', Object.keys(tplItemMap.value).length)
} catch (error: any) {
console.error('加载模板项失败:', error)
VxeUI.modal.message({
content: `加载模板配置失败: ${error.message || '未知错误'}`,
status: 'error',
duration: 3000
})
}
}
const checkData = () => {
drawerVisible.value = true
validationDrawerRef.value?.setValidateData(validFormula.value, formData)
}
const handleInputBlur = (rowCode: string, field: string, formula?: string) => {
if (!formula) return;
const key = getFieldKey(rowCode, field);
const value = formData[key];
if (!value || value === '') {
delete inputErrors.value[key];
return;
}
validateFieldFormula(rowCode, field, formula);
};
const validateFieldFormula = (rowCode: string, field: string, formula: string) => {
const key = getFieldKey(rowCode, field);
try {
const expression = buildExpression(formula, rowCode);
const isValid = eval(expression);
if (!isValid) {
inputErrors.value[key] = {
message: "公式校验失败,请检查填写内容",
formula
};
} else {
delete inputErrors.value[key];
}
} catch (error: any) {
inputErrors.value[key] = {
message: "公式校验失败,请检查填写内容",
formula,
error: error.message
};
}
};
const buildExpression = (formula: string, rowCode: string): string => {
const fieldNames = formula.match(/\b[A-Za-z][A-Za-z0-9_]*\b/g) || [];
const uniqueFields = [...new Set(fieldNames.filter(f =>
!['and', 'or', 'not', 'equal', 'less', 'greater', 'if', 'else', 'true', 'false']
.includes(f.toLowerCase())
))];
uniqueFields.sort((a, b) => b.length - a.length);
let expression = formula;
for (const fieldName of uniqueFields) {
const key = getFieldKey(rowCode, fieldName);
const value = formData[key];
if (value !== undefined && value !== null && value !== '') {
const numValue = Number(value);
if (!isNaN(numValue)) {
expression = expression.replace(
new RegExp(`\\b${fieldName}\\b`, 'g'),
numValue.toString()
);
}
}
}
return expression;
};
const toggleTooltip = (rowCode: string, field: string) => {
const key = getFieldKey(rowCode, field);
if (hoveredKey.value === key) {
showTooltip.value = false;
hoveredKey.value = '';
} else {
showTooltip.value = true;
hoveredKey.value = key;
showErrorTooltip.value = false;
hoveredErrorKey.value = '';
}
};
const toggleErrorTooltip = (rowCode: string, field: string) => {
const key = getFieldKey(rowCode, field);
if (hoveredErrorKey.value === key) {
showErrorTooltip.value = false;
hoveredErrorKey.value = '';
} else {
showErrorTooltip.value = true;
hoveredErrorKey.value = key;
showTooltip.value = false;
hoveredKey.value = '';
}
};
const closeAllTooltips = () => {
showTooltip.value = false;
hoveredKey.value = '';
showErrorTooltip.value = false;
hoveredErrorKey.value = '';
};
const showHelpIcon = (rowCode: string, field: string, item: any): boolean => {
if (!item || item.hasRight === false) {
return false;
}
if (item.hasValidFormula !== undefined) {
return item.hasValidFormula === true;
}
const key = getFieldKey(rowCode, field);
const hasRight = userAllocItems.value.includes(key);
if (!hasRight) return false;
const formulaResult = findEarliestFormulaForField(validFormula.value, field);
return !!formulaResult.formula;
};
const setFormItemRight = () => {
console.log('开始设置表单权限...')
console.log('userAllocItems 数量:', userAllocItems.value?.length || 0)
console.log('validFormula 数量:', validFormula.value?.length || 0)
if (!tableFormData || !Array.isArray(tableFormData)) {
console.warn('tableFormData 不存在或不是数组')
return
}
tableFormData.forEach((row: any) => {
if (row.content && Array.isArray(row.content)) {
row.content.forEach((item: any) => {
if (item && item.field) {
const key = getFieldKey(row.code, item.field)
item.hasRight = userAllocItems.value.includes(key)
if (item.hasRight) {
const formulaResult = findEarliestFormulaForField(validFormula.value, item.field)
item.hasValidFormula = !!formulaResult.formula
item.matchedFormula = formulaResult.formula || null
} else {
item.hasValidFormula = false
item.matchedFormula = null
}
}
})
}
// 设置备注权限
if (row.remarks && row.remarks.field) {
const remarksKey = getFieldKey(row.code, row.remarks.field)
row.remarks.hasRight = userAllocItems.value.includes(remarksKey)
}
})
console.log('表单权限设置完成')
}
const findEarliestFormulaForField = (
formulas: FormulaItem[],
field: string
): { formula: FormulaItem | null, index: number } => {
let result = { formula: null, index: Infinity }
if (!formulas || !Array.isArray(formulas)) {
return result
} }
formulas.forEach((f: FormulaItem, idx: number) => {
if (f && f.formula) {
const text = f.formula
const escapedField = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const regex = new RegExp(`\\b${escapedField}\\b(?!\\w)`, 'g')
const match = regex.exec(text)
if (match && match.index < result.index) {
result = { formula: f, index: match.index }
}
}
})
return result
}
const refreshHelpIcons = () => {
console.log('刷新帮助图标...')
if (!tableFormData || !Array.isArray(tableFormData)) return
tableFormData.forEach((row: any) => {
if (row.content && Array.isArray(row.content)) {
row.content.forEach((item: any) => {
if (item && item.field) {
const key = getFieldKey(row.code, item.field)
const hasRight = userAllocItems.value.includes(key)
if (hasRight) {
const formulaResult = findEarliestFormulaForField(validFormula.value, item.field)
item.hasValidFormula = !!formulaResult.formula
item.matchedFormula = formulaResult.formula || null
} else {
item.hasValidFormula = false
item.matchedFormula = null
}
}
})
}
})
}
const getFieldKey = (pcode: string | undefined, field: string): string => {
return pcode ? `${pcode}_${field}` : field
}
const handleValidationResultClick = (result: any) => {
if (!result) return
const message = `字段 ${result.field || '未知'} 的校验结果: ${result.isValid ? '通过' : '失败'}`
const status = result.isValid ? 'success' : 'error'
VxeUI.modal.message({ content: message, status })
} }
// 监听数据变化,自动校验
watch(formData, (newVal, oldVal) => {
if (!isInitialized.value) return
// 找到变化的字段
const changedKeys: string[] = []
Object.keys(newVal).forEach(key => {
if (newVal[key] !== oldVal[key]) {
changedKeys.push(key)
}
})
// 对新字段进行校验
changedKeys.forEach(key => {
const [rowCode, field] = key.split('_')
if (rowCode && field) {
// 查找对应的校验规则
const row = tableFormData.find((r: any) => r.code === rowCode)
if (row && row.content) {
const item = row.content.find((c: any) => c.field === field)
if (item && item.matchedFormula?.formula) {
handleInputBlur(rowCode, field, item.matchedFormula.formula)
}
}
}
})
}, { deep: true })
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
...@@ -288,7 +767,12 @@ async function setTplItemMap() { ...@@ -288,7 +767,12 @@ async function setTplItemMap() {
} }
.table-input { .table-input {
width: 80px; width: 120px;
margin: 0 2px;
}
.table-textarea {
width: 200px;
margin: 0 2px; margin: 0 2px;
} }
...@@ -301,6 +785,123 @@ async function setTplItemMap() { ...@@ -301,6 +785,123 @@ async function setTplItemMap() {
border-left: none; border-left: none;
border-right: none; border-right: none;
background: transparent; background: transparent;
line-height: 50px; line-height: 1.5;
}
.vxe-textarea {
font-size: 12px;
}
.input-wrapper {
position: relative;
display: inline-block;
vertical-align: middle;
}
.help-icon {
margin-left: 5px;
cursor: pointer;
color: #1890ff;
font-weight: bold;
font-size: 14px;
display: inline-block;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
background: #e6f7ff;
border-radius: 50%;
transition: all 0.3s;
opacity: 0;
animation: fadeIn 0.5s forwards;
animation-delay: 0.3s;
&:hover {
background: #bae7ff;
transform: scale(1.1);
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
.error-icon {
margin-left: 5px;
cursor: pointer;
color: #ff4d4f;
font-size: 16px;
display: inline-block;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
transition: all 0.3s;
&:hover {
transform: scale(1.1);
}
}
.tooltip, .error-tooltip {
position: absolute;
top: 100%;
left: 0;
margin-top: 5px;
padding: 10px;
font-size: 12px;
border-radius: 4px;
z-index: 1000;
white-space: pre-wrap;
max-width: 300px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
pointer-events: none;
}
.tooltip {
background: #333;
color: #fff;
&::before {
content: '';
position: absolute;
bottom: 100%;
left: 10px;
border-width: 5px;
border-style: solid;
border-color: transparent transparent #333 transparent;
}
}
.error-tooltip {
background: #8b0000;
color: #fff;
&::before {
content: '';
position: absolute;
bottom: 100%;
left: 10px;
border-width: 5px;
border-style: solid;
border-color: transparent transparent #8b0000 transparent;
}
}
// 添加必填项标识
.input-wrapper.required::before {
content: '*';
color: #ff4d4f;
position: absolute;
left: -8px;
top: 0;
font-size: 14px;
} }
</style> </style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论