text-align-last: justify 使用方法,对齐字段交互

这篇文章展示了在HTML页面中使用CSS样式创建了一个简单的表单,包含身份证号和密码输入框,用于演示前端用户验证的过程。

在这里插入图片描述

<html>
<style>
  .label {
    display: inline-block;
    width: 100px;
    text-align-last: justify;
  }
</style>

<body>
  <div class="l-content">
    <div>
      <div class="label">身份证:</div>
      <div class="label">xxxx</div>
    </div>
    <div>
      <div class="label">密码:</div>
      <div class="label">xxxx111</div>
    </div>
  </div>
</body>

</html>

<template> <view class="next-tree-view-bottom" :style="{ minHeight: height, paddingLeft: '12px', paddingRight: '12px', backgroundColor: bgColor, paddingBottom: safePaddingBottom }" > <!-- 包裹层控制居中或右对齐 --> <view class="btn-wrapper"> <zy-button v-for="(btn, index) in buttons" :key="index" :class="[ 'next-tree-view-bottom-btn', `next-tree-view-bottom-${btn.type || 'default'}`, btn.disabled ? 'disabled' : '' ]" :loading="loading || btn.loading" :disabled="loading || btn.loading || btn.disabled" :text="btn.text" :type="btn.type || 'primary'" :size="btn.size || 'lg'" @click="handleClick(btn)" > </zy-button> </view> </view> </template> <script setup> import { ref } from 'vue' // 定义 props const props = defineProps({ width: { type: String, default: '100vw' }, height: { type: String, default: '50px' }, bgColor: { type: String, default: '#FFF' }, safeAreaInsetBottom: { type: Boolean, default: true }, safePadding: { type: String, default: '12px' }, buttons: { type: Array, required: true, validator: buttons => Array.isArray(buttons) && buttons.every(btn => typeof btn.text === 'string') }, loading: { type: Boolean, default: false }, debounceTime: { type: Number, default: 0 } }) const emit = defineEmits(['click']) const safePaddingBottom = props.safeAreaInsetBottom ? `max(${props.safePadding}, calc(${props.safePadding} + env(safe-area-inset-bottom)))` : props.safePadding const timers = ref({}) const handleClick = button => { const btnId = button.id ?? button.text if (timers.value[btnId]) clearTimeout(timers.value[btnId]) timers.value[btnId] = setTimeout(() => { emit('click', button) if (typeof button.click === 'function') button.click() }, props.debounceTime) } </script> <style scoped> .next-tree-view-bottom { position: fixed; bottom: 0; left: 0; width: v-bind(width); min-height: 50px; background: #ffffff; z-index: 9999999999; box-sizing: border-box; padding: 12px; /* 安全区已在上面注入 */ } /* 按钮容器:控制对齐方式 */ .btn-wrapper { display: flex; gap: 12px; height: 50px; justify-content: center; /* 默认居中 */ width: 100%; /* 👈 关键:必须占满父容器宽度 */ } /* 大于 600px 时:靠右对齐 */ @media (min-width: 601px) { .btn-wrapper { float: right; width: 30%; } /* 固定按钮宽度 */ .next-tree-view-bottom-btn { width: 200px !important; flex: none !important; } } /* 按钮通用样式 */ .next-tree-view-bottom-btn { height: 50px; line-height: 50px; font-size: 18px; display: flex; justify-content: center; align-items: center; border-radius: 2px; border: none; cursor: pointer; transition: all 0.2s ease; position: relative; } /* 单个按钮:最多占满可用空间(减去间隙) */ .next-tree-view-bottom-btn:first-child:last-child { flex: 1; max-width: calc(100% - 0px); /* 允许撑满 .btn-wrapper */ } /* 两个按钮:每个最多占 (100% - gap)/2 */ .next-tree-view-bottom-btn:nth-child(1):nth-last-child(2), .next-tree-view-bottom-btn:nth-child(2):nth-last-child(1) { flex: 1; max-width: calc((100% - 12px) / 2); } /* 三个及以上:flex: 1 自动均分 */ .next-tree-view-bottom-btn { flex: 1; min-width: 0; } /* 类型化样式 */ .next-tree-view-bottom-default { background: #ffffff; border: 1px solid #e5e5e5; color: #333333; } .next-tree-view-bottom-default:hover:not(:disabled) { background: #f8f8f8; } .next-tree-view-bottom-confirm { background: #0f56d5; color: #ffffff; } .next-tree-view-bottom-confirm:hover:not(:disabled) { background: #0c46b0; } .next-tree-view-bottom-cancel { background: #ffffff; border: 1px solid #e5e5e5; color: #333333; } .next-tree-view-bottom-cancel:hover:not(:disabled) { background: #f8f8f8; } /* 禁用状态 */ .next-tree-view-bottom-btn.disabled, .next-tree-view-bottom-btn:disabled { opacity: 0.6; cursor: not-allowed; } /* 加载动画 */ .spinner { display: inline-block; width: 16px; height: 16px; border: 2px solid #ffffff80; border-top-color: #fff; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } </style> 当前组件直接在<template> <zy-sticky-bottom :buttons="buttons" :safe-area-inset-bottom="true" :safe-padding="'12px'" @click="onButtonClick" /> </template> <script setup> const buttons = [ { text: '取消', type: 'cancel', click: () => console.log('取消被点击') }, { text: '确定', type: 'confirm', click: () => console.log('确定被点击') } ] const onButtonClick = button => { if (button.text == '确定') { console.log('🚀 ~ button.text:', button.text) button.loading = true } } </script> onButtonClick 中修改加载的值没有生效
最新发布
10-21
<template> <!-- 报警自动转工单预案 --> <div class="alarm-work-order"> <template v-if="companyInfo.companyCode"> <a-spin :spinning="isLoading" :delay="500" :tip="$t('加载中...')"> <div class="edit-view"> <div class="input-item"> <div class="input-lable text-bold"> <span>{{ $t('预案是否启用') }}</span> </div> <div class="input-content"> <a-radio-group v-model="formData.enabled" :default-value="1"> <a-radio :value="1">{{ $t('启用') }}</a-radio> <a-radio :value="0">{{ $t('禁用') }}</a-radio> </a-radio-group> </div> </div> <div class="input-item"> <div class="input-lable text-align"> <span> {{ $t('报警类型:') }}</span> </div> <div class="input-content"> <a-select collapseTags mode="multiple" :maxTagCount="2" v-model="formData.type" allowClear placeholder="请选择" optionFilterProp="label" > <a-select-option v-for="(item, index) in typeOptions" :value="item.value" :key="index"> {{ item.label }} </a-select-option> </a-select> </div> </div> <div class="input-item"> <div class="input-lable"> <span> {{ $t('工单派发配置:') }}</span> </div> <div class="table-box" id="tableBox"> <a-table rowKey="department" :columns="columns" :dataSource="displayTableList" :scroll="{ x: '100%' }" :pagination="false" > <template slot="staff" slot-scope="text, record, index"> <a-select v-model="record.staffConfig" mode="multiple" placeholder="选择人员" optionFilterProp="label" :maxTagCount="2" allowClear @change="val => handleStaffChange(val, 'staffConfig', index)" > <a-select-option v-for="person in availableStaffMap[index]" :key="person.id" :label="person.name" :value="person.id" > {{ person.name }} </a-select-option> </a-select> </template> <template slot="time" slot-scope="text, record, index"> <a-select v-model="record.timeConfig" @change="val => changeTime(val, 'timeConfig', index)" placeholder="选择期望完成时间" > <a-select-option v-for="option in timeOptions" :key="option.value" :value="option.value"> {{ option.label }} </a-select-option> </a-select> </template> </a-table> </div> </div> <div class="btn-view action-list"> <a-button @click="onSave" type="primary">{{ $t('确定') }}</a-button> <a-button @click="onCancel" type="normal">{{ $t('取消') }}</a-button> </div> </div> </a-spin> </template> </div> </template> <script> export default { props: { orgInfo: { type: Object, default: () => { return {}; }, }, companyInfo: { type: Object, default: () => { return {}; }, }, }, data() { return { isLoading: false, formData: {}, typeOptions: [ { label: '选项1', value: 1, }, { label: '选项2', value: 2, }, { label: '选项3', value: 3, }, { label: '选项4', value: 4, }, ], columns: [ { title: '部门', key: 'department', dataIndex: 'department', ellipsis: true, }, { title: '用户', dataIndex: 'staff', scopedSlots: { customRender: 'staff' }, key: 'staff', }, { title: '期望完成时间设定', dataIndex: 'time', scopedSlots: { customRender: 'time' }, key: 'time', }, ], tableList: [ { department: '技术部', staffConfig: ['001', '002'], timeConfig: '24h' }, { department: '运维部', staffConfig: ['003'], timeConfig: '48h' }, { department: '安全部', staffConfig: [], timeConfig: '7d' }, { department: '网络部', staffConfig: [], timeConfig: '24h' }, { department: '数据库部', staffConfig: [], timeConfig: '12h' }, ], timeOptions: [ { label: '报警后12小时', value: '12h' }, { label: '报警后24小时', value: '24h' }, { label: '报警后48小时', value: '48h' }, { label: '报警后7日内', value: '7d' }, ], staffList: [ { id: '001', name: '张三' }, { id: '002', name: '李四' }, { id: '003', name: '王五' }, { id: '004', name: '赵六' }, ], }; }, computed: { // 所有被选中的用户ID(整个表格) allSelectedStaffIds() { const selected = new Set(); this.tableList.forEach(row => { if (Array.isArray(row.staffConfig)) { row.staffConfig.forEach(id => selected.add(id)); } }); return selected; }, // 为每一行生成可用人员列表 availableStaffMap() { const map = {}; const allSelected = this.allSelectedStaffIds; this.tableList.forEach((row, index) => { // 当前行已选的人员ID集合 const currentSelected = row.staffConfig ? new Set(row.staffConfig) : new Set(); // 过滤出当前行可用的人员 map[index] = this.staffList.filter(person => !allSelected.has(person.id) || currentSelected.has(person.id)); }); return map; }, // 用于显示的表数据(包含过滤后的人员显示) displayTableList() { return this.tableList.map((row, index) => { const availableStaff = this.availableStaffMap[index] || []; return { ...row, // 仅用于显示,不修改原始数据 displayStaffConfig: row.staffConfig.filter(id => availableStaff.some(p => p.id === id)), }; }); }, }, mounted() { console.log(this.allSelectedStaffIds, 'allSelectedStaffIdsallSelectedStaffIds'); console.log(this.availableStaffMap, 'availableStaffMap'); console.log(this.displayTableList, 'displayTableList'); // this.updateStatus(); }, methods: { changeTime(value, key, index) { this.$set(this.tableList[index], key, value); }, handleStaffChange(value, key, index) { // 人员只能选择两位 if (value && value.length > 2) { this.$message.destroy(); this.$message.warning('人员最多选择2位'); value = value.slice(0, 2); } this.$set(this.tableList[index], key, value); // this.updateStatus(); console.log(this.allSelectedStaffIds, 'allSelectedStaffIdsallSelectedStaffIds'); console.log(this.availableStaffMap, 'availableStaffMap'); console.log(this.displayTableList, 'displayTableList'); }, updateStatus() { const selectedIds = new Set(); this.tableList.forEach(row => { if (Array.isArray(row.staffConfig)) { row.staffConfig.forEach(id => selectedIds.add(id)); } }); this.staffList.forEach((person, index) => { const shouldDisable = selectedIds.has(person.id); if (person.disabled !== shouldDisable) { this.$set(this.staffList, index, { ...person, disabled: shouldDisable, }); } }); }, updateData(value, key, index) { this.$set(this.tableList[index], key, value); }, // 确定 onSave() { console.log('保存的数据:', { tableList: this.tableList, }); }, // 取消 onCancel() {}, }, }; </script> <style lang="less" scoped> .alarm-work-order { height: 100%; width: 100%; position: relative; box-sizing: border-box; .edit-view { width: 100%; padding: 46px 32px 24px 32px; .input-item { width: 100%; display: flex; margin-bottom: 22px; &:last-child { margin-bottom: 0 !important; } } .input-lable { width: 88px; min-width: 88px; justify-content: start; display: flex; color: #808e9d; font-size: 14px; line-height: 19px; margin-right: 24px; span { white-space: nowrap; } } .text-bold { font-weight: bold; color: #4e5c6b; } .text-align { align-items: center; } .btn-view { display: flex; align-items: center; justify-content: flex-start; padding-left: 112px; margin-top: 24px; & > button { margin-right: 12px; } } } } </style> 代码评审
07-09
<think>好的,我现在需要详细解释一下text-align: justify;、text-align-last: justify;和text-align: justify-all;这三个CSS属性。首先,我得回忆一下自己对这些属性的理解,确保没有记错,然后可能需要查证一些细节,特别是关于text-align: justify-all;的,因为好像这个属性不太常用或者可能已经被弃用了? 首先,text-align: justify;的作用是让文本两端对齐,这在中文排版中常见,比如报纸或杂志,左右两边都对齐,看起来更整齐。这个属性会对齐除最后一行外的所有行,最后一行通常是左对齐的,这可能和语言的方向有关,比如在英文中,如果文本方向是从左到右,最后一行左对齐;如果是右到左的语言,最后一行可能右对齐。 接下来是text-align-last: justify;,这个属性专门用来设置最后一行文本的对齐方式。当使用text-align: justify;时,最后一行默认不是两端对齐的,但如果想要最后一行也两端对齐,就需要用到text-align-last: justify;。不过,可能需要考虑浏览器兼容性的问题,比如旧版本的浏览器可能不支持这个属性。 然后是text-align: justify-all;,这个属性我记得在之前的规范中可能被提议过,但可能没有被广泛支持,或者已经被移除了?它的作用可能是让所有行,包括最后一行都两端对齐,类似于同时应用text-align: justify;和text-align-last: justify;的效果。不过需要确认这个属性是否真的存在,或者是否属于某个特定的CSS版本,比如CSS3中的某个模块,或者已经被废弃了。 为了确保准确性,我需要查阅相关的CSS规范或者可靠的文档。例如,MDN Web Docs通常是一个很好的资源。根据MDN,text-align: justify-all;确实存在,但可能属于CSS Text Module Level 3的提案,并且目前浏览器支持非常有限,甚至可能没有实现。它的作用是将所有行强制两端对齐,包括最后一行,但实际使用中可能无法得到支持,因此推荐使用text-align: justify;配合text-align-last: justify;来实现类似的效果。 同时,需要注意不同浏览器对这些属性的支持情况。例如,text-align-last在IE和Edge中可能需要前缀,或者部分版本支持。而justify-all可能几乎没有浏览器支持,所以实际项目中可能无法使用。 此外,还要考虑使用场景。两端对齐可能在中文排版中效果不错,因为每个字等宽,但在英文中,由于单词长度不一,可能会出现单词间距过大的情况,影响可读性。这时候可能需要配合hyphens属性或者使用连字符来优化。 总结一下,回答中需要涵盖以下几点: 1. text-align: justify;的作用和限制(最后一行不对齐)。 2. text-align-last: justify;的作用,如何单独调整最后一行的对齐。 3. text-align: justify-all;的可能作用和兼容性问题,指出其可能不被支持,建议替代方案。 4. 实际应用中的注意事项,比如浏览器兼容性、可读性问题等。 可能还需要提供示例代码,说明如何同时使用text-aligntext-align-last来达到所有行两端对齐的效果。同时,强调justify-all的不实用性,建议避免使用,转而使用其他属性组合。</think>以下是关于 `text-align: justify;`、`text-align-last: justify;` 和 `text-align: justify-all;` 的详细解释: --- ### 1. **`text-align: justify;`** - **作用**:将文本内容**两端对齐**(分散对齐),使段落左右边缘整齐(常见于书籍、报纸排版)。 - **对齐规则**:除**最后一行**外,所有行的左右两端对齐(通过调整字符/单词间距)。 - **最后一行行为**:默认保持自然对齐(左对齐或右对齐,取决于语言方向)。 - **适用场景**:多行文本段落,需要整洁的视觉效果(中文排版常用)。 - **示例**: ```css p { text-align: justify; } ``` --- ### 2. **`text-align-last: justify;`** - **作用**:**单独控制最后一行**的对齐方式,强制其两端对齐- **需配合**:通常与 `text-align: justify;` 一起使用,确保所有行(包括最后一行)对齐- **兼容性**:主流浏览器支持较好,但旧版本可能需要前缀(如 `-moz-text-align-last`)。 - **示例**: ```css p { text-align: justify; text-align-last: justify; } ``` --- ### 3. **`text-align: justify-all;`** - **作用**:**强制所有行(包括最后一行)两端对齐**。 - **现状**:属于 CSS Text Module Level 3 提案,**浏览器支持极差**(几乎无实际应用)。 - **替代方案**:使用 `text-align: justify; + text-align-last: justify;` 组合实现类似效果。 - **示例**(理论上): ```css p { text-align: justify-all; /* 不推荐,缺乏支持 */ } ``` --- ### 注意事项 1. **中英文差异**: - 中文:两端对齐效果较好(字符等宽)。 - 英文:可能导致单词间距过大,可配合 `hyphens: auto;` 添加连字符优化。 2. **兼容性**: - `text-align-last: justify;` 在 IE11+ 和现代浏览器中支持,但需测试目标平台。 - `justify-all` 不建议使用--- ### 总结 - **标准两端对齐**:`text-align: justify;` - **包含最后一行对齐**:组合 `text-align: justify;` + `text-align-last: justify;` - **避免使用 `justify-all`**:因兼容性问题,改用替代方案。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值