修改河南项目的管理后台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