办公V3
This commit is contained in:
@@ -7,49 +7,156 @@
|
||||
<el-button size="mini" icon="el-icon-arrow-left" @click="$router.back()">返回</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" size="small">
|
||||
<el-form-item label="申请人" prop="empId">
|
||||
<el-select v-model="form.empId" filterable placeholder="选择申请人" style="width: 100%">
|
||||
<el-option
|
||||
v-for="emp in employees"
|
||||
:key="emp.empId"
|
||||
:label="emp.empName || emp.empNo || emp.empId"
|
||||
:value="emp.empId"
|
||||
/>
|
||||
</el-select>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" size="small" class="metal-form">
|
||||
<!-- 顶部摘要 -->
|
||||
<div class="form-summary">
|
||||
<div class="summary-left">
|
||||
<div class="summary-title">发起出差</div>
|
||||
<div class="summary-sub">请完善信息后提交,系统将按流程节点流转</div>
|
||||
</div>
|
||||
<div class="summary-right">
|
||||
<div class="summary-item">
|
||||
<div class="k">申请人</div>
|
||||
<div class="v">{{ currentApplicantText }}</div>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<div class="k">目的地</div>
|
||||
<div class="v">{{ form.destination || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="14">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出差类型" prop="travelType">
|
||||
<el-select
|
||||
v-model="form.travelType"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
clearable
|
||||
placeholder="选择或输入(如:客户拜访/项目支持/培训学习)"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option v-for="t in travelTypeOptions" :key="t" :label="t" :value="t" />
|
||||
</el-select>
|
||||
<div class="hint-text">优先选择;若公司类型未配置,可直接输入</div>
|
||||
</el-form-item>
|
||||
<el-row :gutter="12">
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="block-title">出差时间</div>
|
||||
<el-row :gutter="14">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker v-model="form.startTime" type="datetime" placeholder="开始时间" style="width: 100%" />
|
||||
<el-date-picker v-model="form.startTime" type="datetime" placeholder="请选择开始时间" style="width: 100%" :picker-options="pickerOptions" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker v-model="form.endTime" type="datetime" placeholder="结束时间" style="width: 100%" />
|
||||
<el-date-picker v-model="form.endTime" type="datetime" placeholder="请选择结束时间" style="width: 100%" :picker-options="pickerOptions" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="目的地" prop="destination">
|
||||
<el-input v-model="form.destination" placeholder="城市/地址" />
|
||||
<el-input v-model="form.destination" placeholder="城市/地址/项目现场" />
|
||||
<div class="hint-text">请填写具体目的地,便于审批人判断出差必要性</div>
|
||||
</el-form-item>
|
||||
|
||||
<div class="block-title">出差说明</div>
|
||||
<el-form-item label="事由" prop="reason">
|
||||
<el-input v-model="form.reason" type="textarea" :rows="3" placeholder="填写出差事由" />
|
||||
<el-input v-model="form.reason" type="textarea" :rows="4" placeholder="请说明出差目的、任务目标、预期成果等" show-word-limit maxlength="200" />
|
||||
</el-form-item>
|
||||
<el-form-item label="差旅附件" prop="travelFileIds">
|
||||
|
||||
<el-form-item label="交通/住宿/行程附件" prop="accessoryApplyIds">
|
||||
<file-upload
|
||||
v-model="form.travelFileIds"
|
||||
v-model="form.accessoryApplyIds"
|
||||
:limit="8"
|
||||
:file-size="50"
|
||||
:file-type="['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx']"
|
||||
multiple
|
||||
/>
|
||||
<div class="hint-text">上传机票、酒店、行程单等(pdf/jpg/png/doc/docx)</div>
|
||||
<div class="hint-text">上传机票、酒店、行程单等(pdf/jpg/png/doc/docx),便于审批与后续报销</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 审批方式(模板/自选审批人) -->
|
||||
<div class="block-title">审批方式</div>
|
||||
<div class="approve-mode">
|
||||
<el-radio-group v-model="approverMode" size="small" @change="onApproverModeChange">
|
||||
<el-radio-button label="template">使用模板流程</el-radio-button>
|
||||
<el-radio-button label="manual">手动选择审批人(一次审批)</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<div class="approve-panel">
|
||||
<div v-if="approverMode === 'template'">
|
||||
<div class="approve-row">
|
||||
<div class="k">流程模板</div>
|
||||
<div class="v">
|
||||
<el-select
|
||||
v-model="tplId"
|
||||
size="small"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择流程模板"
|
||||
style="width: 360px"
|
||||
@change="onTplChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="t in availableTpls"
|
||||
:key="t.tplId"
|
||||
:label="`${t.tplName}${t.version ? ' (v' + t.version + ')' : ''}`"
|
||||
:value="t.tplId"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hint-text">提示:选择模板后,将按模板节点自动流转(含抄送节点)。</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div class="approve-row">
|
||||
<div class="k">审批人</div>
|
||||
<div class="v" style="max-width: 520px">
|
||||
<el-button size="mini" type="primary" plain @click="openUserSelect">选择审批人</el-button>
|
||||
<span style="margin-left: 10px; font-weight: 600; color: #2b2f36">
|
||||
{{ assigneeUserName || '未选择' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hint-text">提示:手动选择审批人将创建一次性审批流程,审批通过后流程立即结束。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form-item label="回执附件(可选)" prop="accessoryReceiptIds">
|
||||
<file-upload
|
||||
v-model="form.accessoryReceiptIds"
|
||||
:limit="8"
|
||||
:file-size="50"
|
||||
:file-type="['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx']"
|
||||
multiple
|
||||
/>
|
||||
<div class="hint-text">可选:上传回执、发票、盖章回单等(审核/归档使用)</div>
|
||||
</el-form-item>
|
||||
|
||||
<div class="block-title">费用信息</div>
|
||||
<el-row :gutter="14">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="收款人" prop="payeeName">
|
||||
<el-input v-model="form.payeeName" placeholder="收款人姓名/公司" />
|
||||
<div class="hint-text">出差费用报销收款方</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预估费用">
|
||||
<el-input-number v-model="form.estimatedCost" :min="0" :step="100" style="width: 100%" />
|
||||
<div class="hint-text">预估总费用(元),便于预算控制</div>
|
||||
</el-form-item>
|
||||
<el-row :gutter="12">
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="14">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行" prop="bankName">
|
||||
<el-input v-model="form.bankName" placeholder="XX银行XX支行" />
|
||||
@@ -61,48 +168,115 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="可选" />
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="可选:补充说明、特殊要求等" show-word-limit maxlength="200" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 提交流程提示:按“真实节点配置 / 手动一次性审批”预览 -->
|
||||
<div class="flow-preview" v-loading="flowLoading">
|
||||
<div class="flow-title">流程预览</div>
|
||||
<div class="flow-sub">
|
||||
<template v-if="approverMode === 'template'">
|
||||
<span v-if="flowTpl">当前模板:{{ flowTpl.tplName }}(v{{ flowTpl.version || 1 }})</span>
|
||||
<span v-else>请选择流程模板</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>一次性审批(手动指定审批人)</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 模板模式 -->
|
||||
<div v-if="approverMode === 'template'">
|
||||
<div v-if="flowNodes && flowNodes.length" class="flow-steps">
|
||||
<div class="flow-step"><div class="dot"></div><div class="txt">填写申请</div></div>
|
||||
<div class="line"></div>
|
||||
<div class="flow-step"><div class="dot"></div><div class="txt">提交</div></div>
|
||||
<template v-for="(n, idx) in flowNodes">
|
||||
<div :key="`line-${n.nodeId || idx}`" class="line"></div>
|
||||
<div :key="`node-${n.nodeId || idx}`" class="flow-step">
|
||||
<div class="dot" :class="{ success: idx === flowNodes.length - 1 }"></div>
|
||||
<div class="txt">{{ nodePreviewText(n, idx) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else class="flow-fallback">
|
||||
<div class="hint-text">提示:请选择一个模板后将展示对应节点预览。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 手动审批模式 -->
|
||||
<div v-else class="flow-steps">
|
||||
<div class="flow-step"><div class="dot"></div><div class="txt">填写申请</div></div>
|
||||
<div class="line"></div>
|
||||
<div class="flow-step"><div class="dot"></div><div class="txt">提交审批({{ assigneeUserName || '请选择' }})</div></div>
|
||||
<div class="line"></div>
|
||||
<div class="flow-step"><div class="dot success"></div><div class="txt">审批结束</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<el-button @click="$router.back()">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submit">提交申请</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 用户选择组件始终挂载 -->
|
||||
<UserSelect ref="userSelect" @onSelected="onUserSelected" />
|
||||
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listEmployee, addTravelReq } from '@/api/hrm'
|
||||
import { addTravelReq } from '@/api/hrm'
|
||||
import { listFlowTemplate, listFlowNode } from '@/api/hrm/flow'
|
||||
import UserSelect from '@/components/userSelect/single.vue'
|
||||
import FileUpload from '@/components/FileUpload'
|
||||
import { getEmployeeByUserId } from '@/api/hrm/employee'
|
||||
|
||||
export default {
|
||||
name: 'HrmTravelRequest',
|
||||
components: { FileUpload },
|
||||
components: {
|
||||
UserSelect,
|
||||
FileUpload
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
employees: [],
|
||||
currentEmp: null,
|
||||
submitting: false,
|
||||
flowLoading: false,
|
||||
flowTpl: null,
|
||||
flowNodes: [],
|
||||
approverMode: 'template',
|
||||
availableTpls: [],
|
||||
tplId: null,
|
||||
assigneeUserId: null,
|
||||
assigneeUserName: '',
|
||||
travelTypeOptions: ['客户拜访', '项目支持', '培训学习', '会议会展', '验收交付', '其他'],
|
||||
pickerOptions: { disabledDate: () => false },
|
||||
form: {
|
||||
empId: '',
|
||||
travelType: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
destination: '',
|
||||
reason: '',
|
||||
travelFileIds: '',
|
||||
accessoryApplyIds: '',
|
||||
accessoryReceiptIds: '',
|
||||
payeeName: '',
|
||||
estimatedCost: 0,
|
||||
bankName: '',
|
||||
bankAccount: '',
|
||||
remark: ''
|
||||
},
|
||||
rules: {
|
||||
empId: [{ required: true, message: '请选择申请人', trigger: 'change' }],
|
||||
travelType: [{ required: true, message: '请选择/输入出差类型', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
|
||||
destination: [{ required: true, message: '请输入目的地', trigger: 'blur' }],
|
||||
reason: [{ required: true, message: '请输入事由', trigger: 'blur' }],
|
||||
travelFileIds: [{ required: true, message: '请上传差旅附件', trigger: 'change' }],
|
||||
accessoryApplyIds: [{ required: true, message: '请上传交通/住宿/行程附件', trigger: 'change' }],
|
||||
payeeName: [{ required: true, message: '请输入收款人', trigger: 'blur' }],
|
||||
bankName: [{ required: true, message: '请输入开户行', trigger: 'blur' }],
|
||||
bankAccount: [{ required: true, message: '请输入银行账号', trigger: 'blur' }]
|
||||
@@ -110,29 +284,151 @@ export default {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadEmployees()
|
||||
const userId = this.$store?.state?.user?.userId
|
||||
if (userId) this.form.empId = userId
|
||||
this.loadCurrentEmployee()
|
||||
this.loadTemplates()
|
||||
},
|
||||
computed: {
|
||||
currentApplicantText() {
|
||||
if (this.currentEmp) return this.formatEmpLabel(this.currentEmp)
|
||||
const user = this.$store?.state?.user || {}
|
||||
return user.nickName || user.userName || '加载中...'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadEmployees() {
|
||||
listEmployee({ pageNum: 1, pageSize: 500 }).then(res => {
|
||||
this.employees = res.rows || res.data || []
|
||||
})
|
||||
async loadTemplates() {
|
||||
try {
|
||||
const res = await listFlowTemplate({ pageNum: 1, pageSize: 200, bizType: 'travel', enabled: 1 })
|
||||
this.availableTpls = res.rows || res.data || []
|
||||
if (!this.tplId && this.availableTpls.length) {
|
||||
this.tplId = this.availableTpls[0].tplId
|
||||
}
|
||||
await this.refreshFlowPreview()
|
||||
} catch (err) {
|
||||
this.availableTpls = []
|
||||
}
|
||||
},
|
||||
async refreshFlowPreview() {
|
||||
this.flowLoading = true
|
||||
try {
|
||||
if (this.approverMode === 'manual') {
|
||||
this.flowTpl = null
|
||||
this.flowNodes = []
|
||||
return
|
||||
}
|
||||
if (!this.tplId) {
|
||||
this.flowTpl = null
|
||||
this.flowNodes = []
|
||||
return
|
||||
}
|
||||
this.flowTpl = this.availableTpls.find(t => t.tplId === this.tplId) || null
|
||||
const nodeRes = await listFlowNode({ pageNum: 1, pageSize: 500, tplId: this.tplId })
|
||||
this.flowNodes = (nodeRes.rows || nodeRes.data || []).filter(n => n.nodeType !== 'end').sort((a, b) => (a.orderNo || 0) - (b.orderNo || 0))
|
||||
} finally {
|
||||
this.flowLoading = false
|
||||
}
|
||||
},
|
||||
async onTplChange(val) {
|
||||
this.tplId = val
|
||||
await this.refreshFlowPreview()
|
||||
},
|
||||
onApproverModeChange(val) {
|
||||
this.approverMode = val
|
||||
if (val === 'manual') this.tplId = null
|
||||
this.refreshFlowPreview()
|
||||
},
|
||||
openUserSelect() {
|
||||
this.$refs.userSelect.open()
|
||||
},
|
||||
onUserSelected(row) {
|
||||
if (row) {
|
||||
this.assigneeUserId = row.userId
|
||||
this.assigneeUserName = row.nickName || row.userName || row.userId
|
||||
this.refreshFlowPreview()
|
||||
}
|
||||
},
|
||||
nodePreviewText(n, idx) {
|
||||
const typeMap = { approve: '审批', cc: '抄送' }
|
||||
const ruleMap = { fixed_user: '指定人员', role: '指定角色', position: '指定岗位', leader: '直属上级' }
|
||||
const nodeType = typeMap[n.nodeType] || '节点'
|
||||
const rule = ruleMap[n.approverRule] || '规则'
|
||||
let detail = ''
|
||||
try {
|
||||
const arr = Array.isArray(n.approverValue) ? n.approverValue : JSON.parse(n.approverValue || '[]')
|
||||
if (arr.length) detail = `:${arr.join('、')}`
|
||||
} catch (e) { detail = n.approverValue ? `:${n.approverValue}` : '' }
|
||||
const text = `${nodeType}(${rule}${detail})`
|
||||
return idx === this.flowNodes.length - 1 ? `${text} → 结束` : text
|
||||
},
|
||||
normalizeOssIds(val) {
|
||||
if (!val) return ''
|
||||
if (typeof val === 'string') return val
|
||||
if (Array.isArray(val)) {
|
||||
const ids = val.map(x => (x && typeof x === 'object') ? (x.ossId ?? x.id ?? x.value) : x).filter(Boolean)
|
||||
return ids.join(',')
|
||||
}
|
||||
return String(val)
|
||||
},
|
||||
formatEmpLabel(emp) {
|
||||
const name = emp.empName || emp.nickName || emp.userName || ''
|
||||
const no = emp.empNo ? ` · ${emp.empNo}` : ''
|
||||
const dept = emp.deptName ? ` · ${emp.deptName}` : ''
|
||||
return `${name || '员工'}${no}${dept}`.trim()
|
||||
},
|
||||
async loadCurrentEmployee() {
|
||||
const userId = this.$store?.state?.user?.id
|
||||
if (!userId) {
|
||||
this.$message.error('无法获取当前用户信息,请重新登录')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await getEmployeeByUserId(userId)
|
||||
if (res.code === 200 && res.data) {
|
||||
this.currentEmp = res.data
|
||||
this.form.empId = res.data.empId
|
||||
} else {
|
||||
this.$message.error('未找到当前用户对应的员工信息,请在员工管理中关联系统用户')
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('加载员工信息失败,请稍后重试')
|
||||
}
|
||||
},
|
||||
submit() {
|
||||
this.$refs.formRef.validate(valid => {
|
||||
this.$refs.formRef.validate(async valid => {
|
||||
if (!valid) return
|
||||
if (this.approverMode === 'template' && !this.tplId) {
|
||||
return this.$message.warning('请选择一个流程模板')
|
||||
}
|
||||
if (this.approverMode === 'manual' && !this.assigneeUserId) {
|
||||
return this.$message.warning('请选择审批人')
|
||||
}
|
||||
this.submitting = true
|
||||
const payload = { ...this.form }
|
||||
addTravelReq(payload)
|
||||
.then(() => {
|
||||
const payload = {
|
||||
empId: this.form.empId,
|
||||
travelType: this.form.travelType,
|
||||
startTime: this.form.startTime,
|
||||
endTime: this.form.endTime,
|
||||
destination: this.form.destination,
|
||||
reason: this.form.reason,
|
||||
accessoryApplyIds: this.normalizeOssIds(this.form.accessoryApplyIds),
|
||||
accessoryReceiptIds: this.normalizeOssIds(this.form.accessoryReceiptIds),
|
||||
payeeName: this.form.payeeName,
|
||||
estimatedCost: this.form.estimatedCost,
|
||||
status: 'pending',
|
||||
bankName: this.form.bankName,
|
||||
bankAccount: this.form.bankAccount,
|
||||
remark: this.form.remark,
|
||||
tplId: this.tplId,
|
||||
manualAssigneeUserId: this.assigneeUserId
|
||||
}
|
||||
try {
|
||||
await addTravelReq(payload)
|
||||
this.$message.success('提交成功')
|
||||
this.$router.push('/hrm/requests')
|
||||
})
|
||||
.finally(() => {
|
||||
} catch (e) {
|
||||
this.$message.error('提交失败,请稍后重试')
|
||||
} finally {
|
||||
this.submitting = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -145,23 +441,98 @@ export default {
|
||||
background: #f8f9fb;
|
||||
}
|
||||
.form-card {
|
||||
max-width: 800px;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
border: 1px solid #d7d9df;
|
||||
border-radius: 12px;
|
||||
background: #ffffff;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
color: #2b2f36;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.metal-form { padding-right: 8px; }
|
||||
.block-title {
|
||||
margin: 20px 0 12px;
|
||||
padding-left: 10px;
|
||||
font-weight: 700;
|
||||
color: #2f3440;
|
||||
border-left: 3px solid #9aa3b2;
|
||||
}
|
||||
.hint-text {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: #8a8f99;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.form-summary {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 12px;
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid #e6e8ed;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(180deg, #ffffff 0%, #fbfcfe 100%);
|
||||
}
|
||||
.summary-title { font-size: 16px; font-weight: 800; color: #2b2f36; }
|
||||
.summary-sub { margin-top: 4px; font-size: 12px; color: #8a8f99; }
|
||||
.summary-right { display: flex; gap: 16px; }
|
||||
.summary-item .k { font-size: 12px; color: #8a8f99; }
|
||||
.summary-item .v { margin-top: 2px; font-weight: 700; color: #2b2f36; }
|
||||
.approve-mode {
|
||||
padding: 12px;
|
||||
border: 1px solid #e6e8ed;
|
||||
border-radius: 10px;
|
||||
background: #fcfdff;
|
||||
}
|
||||
.approve-panel { margin-top: 12px; }
|
||||
.approve-row { display: flex; align-items: center; gap: 12px; }
|
||||
.approve-row .k { font-size: 14px; color: #606266; }
|
||||
.flow-preview {
|
||||
margin-top: 20px;
|
||||
padding: 12px;
|
||||
border: 1px solid #e6e8ed;
|
||||
border-radius: 10px;
|
||||
background: #fcfdff;
|
||||
}
|
||||
.flow-title { font-weight: 800; color: #2b2f36; }
|
||||
.flow-sub { margin-top: 4px; font-size: 12px; color: #8a8f99; }
|
||||
.flow-steps {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.flow-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #e6e8ed;
|
||||
background: #fff;
|
||||
}
|
||||
.flow-step .dot { width: 8px; height: 8px; border-radius: 50%; background: #9aa3b2; }
|
||||
.flow-step .dot.success { background: #67c23a; }
|
||||
.flow-step .txt { font-size: 12px; color: #2b2f36; font-weight: 600; }
|
||||
.flow-steps .line { width: 26px; height: 1px; background: #e6e8ed; }
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
@media (max-width: 1200px) {
|
||||
.summary-right { display: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user