修改河南项目的管理后台ui
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<!--
|
||||
胶囊开关(替代 el-switch,用于后台所有布尔/状态列)
|
||||
|
||||
## 为什么自己写一个,而不是继续改 el-switch 的样式
|
||||
|
||||
Element 的 el-switch 把「轨道」「滑块」和「左右两段文字」拆成四个各自定位的元素,
|
||||
文字用 is-active 控制显隐、轨道用 CSS 变量上色。后台原先 17 个页面各自拿
|
||||
::v-deep 去覆盖它,改出来的东西越修越乱(滑块跑出轨道、两段文字同时出现),
|
||||
**连续三次返工都没收敛**。这里改成自己拼 DOM:一个轨道容器 + 一个滑块 + 一段文字,
|
||||
任何时刻只有一段文字、位置全部由本组件算好,不再依赖 Element 内部结构。
|
||||
|
||||
## 视觉规则(用户 2026-09-24 定的口径)
|
||||
|
||||
| 值 | 轨道 | 滑块 | 文字 |
|
||||
|---|---|---|---|
|
||||
| 真(生效) | 绿 #457450 | 白圆点停在**右侧** | 「启用」白字,落在左半边 |
|
||||
| 假(未生效) | 白 + 细描边 | 白圆点停在**左侧** | 「禁用」灰字,落在右半边 |
|
||||
|
||||
⇒ **绿 = 该项生效,白 = 该项未生效**;文字是结果,整颗开关只出现一次。
|
||||
|
||||
## 尺寸要点(改尺寸必读)
|
||||
|
||||
容器 `66×26`;滑块 `22` 停在 `left:2`(开启 `translateX(38px)`);文字框宽 `36`,
|
||||
关闭态 `left:26`、开启态 `left:3` —— **文字只在滑块不在的那半边**,两态重叠宽度都是 0。
|
||||
|
||||
⚠️ 早期版本是 54px 宽、文字框横跨整条轨道(`left:11; width:34`),结果**滑块压住文字**
|
||||
(用户 2026-09-24 反馈「启用」的「用」被盖掉)。**根因是文字框与滑块占用了同一段横向空间**,
|
||||
不是 z-index 或颜色问题。加宽容器把两者分到各半边才根治。
|
||||
|
||||
## 用法(与 el-switch 的 v-model 语义一致,可直接替换)
|
||||
|
||||
<capsule-switch v-model="scope.row.status" @change="save(scope.row)" />
|
||||
// 自定义两端文案
|
||||
<capsule-switch v-model="row.isDefault" active-text="是" inactive-text="否" />
|
||||
// 库里是 '0'/'1' 字符串时(Element 写法兼容)
|
||||
<capsule-switch v-model="row.status" active-value="0" inactive-value="1" />
|
||||
|
||||
若页面用 `:active-value` / `:inactive-value` 传了非布尔值,本组件内部把
|
||||
传入值与 activeValue 比较得出开关位置,change 时再回吐对应的原始值,
|
||||
⇒ 父组件拿到的仍然是原来的 '0'/'1',**不必改后端契约**。
|
||||
-->
|
||||
<div
|
||||
class="capsule-switch"
|
||||
:class="{ 'is-on': isOn, 'is-disabled': disabled }"
|
||||
role="switch"
|
||||
:aria-checked="isOn"
|
||||
:aria-disabled="disabled"
|
||||
tabindex="0"
|
||||
@click="toggle"
|
||||
@keydown.enter.prevent="toggle"
|
||||
@keydown.space.prevent="toggle"
|
||||
>
|
||||
<span class="capsule-switch__text">{{ isOn ? activeText : inactiveText }}</span>
|
||||
<span class="capsule-switch__dot"></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CapsuleSwitch',
|
||||
props: {
|
||||
// 兼容 el-switch:父组件既可能传布尔,也可能传 '0'/'1'
|
||||
value: {
|
||||
type: [Boolean, String, Number],
|
||||
default: false
|
||||
},
|
||||
activeValue: {
|
||||
type: [Boolean, String, Number],
|
||||
default: true
|
||||
},
|
||||
inactiveValue: {
|
||||
type: [Boolean, String, Number],
|
||||
default: false
|
||||
},
|
||||
activeText: {
|
||||
type: String,
|
||||
default: '启用'
|
||||
},
|
||||
inactiveText: {
|
||||
type: String,
|
||||
default: '禁用'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isOn() {
|
||||
return this.value === this.activeValue;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
if (this.disabled) return;
|
||||
const next = this.isOn ? this.inactiveValue : this.activeValue;
|
||||
this.$emit('input', next);
|
||||
this.$emit('change', next);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 尺寸常量:宽 66、高 26,滑块 22,两端留 2px 缝
|
||||
⚠️ 改尺寸必须重算 —— 约束是「文字不能被滑块压住」:
|
||||
关闭:滑块停在左(x 3~25),文字只占右半边(x 27~63),两者不重叠
|
||||
开启:滑块停在右(x 41~63),文字只占左半边(x 3~39),两者不重叠
|
||||
容器宽度必须 ≥ 滑块(22) + 文字所需宽度(36) + 两侧留白(约 8),
|
||||
54px 那版文字框横跨整条轨道 ⇒ 必然被滑块压住(用户 2026-09-24 反馈的正是这个)。 */
|
||||
.capsule-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 66px;
|
||||
height: 26px;
|
||||
border-radius: 13px;
|
||||
border: 1px solid #d8c3b0;
|
||||
background: #fffdfa;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
transition: background-color 0.3s, border-color 0.3s;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
|
||||
&:focus-visible {
|
||||
box-shadow: 0 0 0 3px rgba(69, 116, 80, 0.22);
|
||||
}
|
||||
|
||||
/* 滑块:白色正圆 */
|
||||
&__dot {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 2px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(139, 115, 85, 0.3);
|
||||
transition: transform 0.3s;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 文字:整颗开关只渲染这一段,换的是内容不是显隐 ⇒ 不可能出现两段文字。
|
||||
⚠️ 必须 z-index 高于滑块,否则关闭态下会被白圆点盖住(「启用」= 绿底白字,压在绿块上反而难读)。 */
|
||||
&__text {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 26px;
|
||||
width: 36px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
color: #8b755a;
|
||||
transition: color 0.3s, left 0.3s;
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&__dot,
|
||||
&__text {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ---- 开启:绿底 + 滑块右移 38(2 → 40),文字换到左半边并改白 ---- */
|
||||
&.is-on {
|
||||
border-color: #457450;
|
||||
background: #457450;
|
||||
|
||||
.capsule-switch__dot {
|
||||
transform: translateX(38px);
|
||||
}
|
||||
|
||||
.capsule-switch__text {
|
||||
left: 3px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user