修复密码输入可访问性并统一新手机号为中国大陆区号

This commit is contained in:
cieanyau committed 2026-11-06 04:53:45 +08:00
1 parent 990b802553
commit f6ab3c9087
15 files changed
+592 -362

No files matched your search

+1 -1
View File
@@ -62,7 +62,7 @@ import PaymentDialog from "~/components/PaymentDialog.vue";
import { t as useListPage_t } from "~/lib/useListPage.js";
import { r as instance_status_r } from "~/lib/instance-status.js";
import { t as instance_status_t } from "~/lib/instance-status.js";
import password from "primevue/password";
import password from "~/components/PasswordField.vue";
import textarea from "primevue/textarea";
import menu from "primevue/menu";
import tag from "primevue/tag";
+95
View File
@@ -0,0 +1,95 @@
<script setup>
/** 复用 PrimeVue 的密码/反馈状态,只统一原生输入语义与可键盘访问的显隐操作。 */
import { computed, nextTick, ref, useSlots } from "vue";
import Password from "primevue/password";
defineOptions({ inheritAttrs: false });
const props = defineProps({
autocomplete: { type: String, default: undefined },
inputProps: { type: Object, default: () => ({}) },
disabled: Boolean,
});
const slots = useSlots();
const forwardedSlots = () =>
Object.keys(slots).filter(
(name) => !["maskicon", "unmaskicon"].includes(name),
);
// autocomplete 属于真实 input,而不是 Password 的包装 DIV;保留已有 aria 等输入属性。
const nativeProps = computed(() => ({
...props.inputProps,
autocomplete: props.autocomplete ?? props.inputProps?.autocomplete ?? "off",
}));
const toggleButton = ref(null);
async function togglePassword(callback) {
callback();
// PrimeVue切换两个显隐slot时会替换按钮;把焦点交回新按钮,键盘才能连续显示/隐藏。
await nextTick();
toggleButton.value?.focus();
}
</script>
<template>
<Password
v-bind="$attrs"
class="password-field"
:input-props="nativeProps"
:disabled="disabled"
>
<template v-for="name in forwardedSlots()" #[name]="scope">
<slot :name="name" v-bind="scope" />
</template>
<template #unmaskicon="{ toggleCallback, class: iconClass }">
<button
ref="toggleButton"
type="button"
:class="['password-toggle', iconClass]"
aria-label="显示密码"
aria-pressed="false"
:disabled="disabled"
@click="togglePassword(toggleCallback)"
>
<i class="pi pi-eye" aria-hidden="true" />
</button>
</template>
<template #maskicon="{ toggleCallback, class: iconClass }">
<button
ref="toggleButton"
type="button"
:class="['password-toggle', iconClass]"
aria-label="隐藏密码"
aria-pressed="true"
:disabled="disabled"
@click="togglePassword(toggleCallback)"
>
<i class="pi pi-eye-slash" aria-hidden="true" />
</button>
</template>
</Password>
</template>
<style scoped>
.password-field .password-toggle {
position: absolute;
inset-block: 0;
inset-inline-end: 0;
display: grid;
place-items: center;
width: 44px;
height: 100%;
min-height: 0;
margin: 0;
padding: 0;
color: var(--p-text-muted-color);
background: transparent;
border: 0;
border-radius: var(--p-form-field-border-radius, 6px);
cursor: pointer;
}
.password-field .password-toggle:focus-visible {
outline: 2px solid var(--p-primary-color);
outline-offset: -3px;
}
.password-field .password-toggle:disabled {
cursor: not-allowed;
}
</style>
+6 -173
View File
@@ -1,175 +1,8 @@
<script>
/** 国际电话区号输入;地区列表按需请求,加载失败仍允许直接填写区号,不阻塞账号表单。 */
<script setup>
/** 新输入手机号只支持中国大陆,区号由请求方固定为86,不再提供地区选择或加载国家列表。 */
import "~/assets/styles/PhoneCodeField.CwyHXfML.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
import { useId } from "vue";
import { onBeforeUnmount } from "vue";
import { ref } from "vue";
import { createCommentVNode } from "vue";
import { createElementBlock } from "vue";
import { Fragment } from "vue";
import { createElementVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { openBlock } from "vue";
import { useApi } from "~/lib/auth.js";
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
var m = {
class: `phone-code-field`,
},
h = [`for`],
g = {
class: `phone-code-input`,
},
_ = [`id`, `value`, `list`, `disabled`],
v = [`id`],
y = [`value`],
b = {
key: 0,
role: `status`,
},
x = {
key: 1,
role: `status`,
},
S = Object.assign(
_plugin_vue_export_helper_t(
defineComponent({
__name: `PhoneCodeField`,
props: {
modelValue: {},
disabled: {
type: Boolean,
},
},
emits: [`update:modelValue`],
setup(u, { emit: p }) {
let S = u,
C = p,
w = useId(),
T = useApi(),
E = ref([]),
D = ref(!1),
O = ref(``),
k = !1;
onBeforeUnmount(() => {
k = !0;
});
async function A() {
// 地区列表仅收录 1–4 位且首位非零的区号,成功列表可复用;卸载后不回写。
if (!(D.value || E.value.length)) {
((D.value = !0), (O.value = ``));
try {
let e = await T.get(`/country`, void 0, !1);
if (k) return;
E.value = (e.list || [])
.filter((e) => /^[1-9]\d{0,3}$/.test(String(e.phone_code)))
.map((e) => ({
phone_code: String(e.phone_code),
name: e.name_zh || e.name || ``,
}));
} catch {
k || (O.value = `地区列表暂不可用,可直接填写国际区号`);
} finally {
k || (D.value = !1);
}
}
}
return (n, r) => (
openBlock(),
createElementBlock(`div`, m, [
createElementVNode(
`label`,
{
for: `${unref(w)}-phone-code`,
},
`国际区号`,
8,
h,
),
createElementVNode(`div`, g, [
(r[1] ||= createElementVNode(
`span`,
{
"aria-hidden": `true`,
},
`+`,
-1,
)),
createElementVNode(
`input`,
{
id: `${unref(w)}-phone-code`,
value: S.modelValue,
list: `${unref(w)}-countries`,
inputmode: `numeric`,
autocomplete: `tel-country-code`,
maxlength: `4`,
disabled: u.disabled,
onFocus: A,
onInput: (r[0] ||= (e) =>
C(
`update:modelValue`,
e.target.value.replace(/^\+/, ``).trim(),
)),
},
null,
40,
_,
),
]),
createElementVNode(
`datalist`,
{
id: `${unref(w)}-countries`,
},
[
(openBlock(!0),
createElementBlock(
Fragment,
null,
renderList(
unref(E),
(t, n) => (
openBlock(),
createElementBlock(
`option`,
{
key: `${t.phone_code}-${n}`,
value: t.phone_code,
},
toDisplayString(t.name) +
` +` +
toDisplayString(t.phone_code),
9,
y,
)
),
),
128,
)),
],
8,
v,
),
unref(D)
? (openBlock(), createElementBlock(`small`, b, `正在加载地区…`))
: createCommentVNode(``, !0),
unref(O)
? (openBlock(),
createElementBlock(`small`, x, toDisplayString(unref(O)), 1))
: createCommentVNode(``, !0),
])
);
},
}),
[[`__scopeId`, `data-v-24cb0489`]],
),
{
__name: `PhoneCodeField`,
},
);
export {};
export default S;
</script>
<template>
<span class="phone-code-field" data-v-24cb0489>中国大陆 +86</span>
</template>
+2 -1
View File
@@ -44,7 +44,7 @@ import checkbox from "primevue/checkbox";
import OsIcon from "~/components/OsIcon.vue";
import OperationNotice from "~/components/OperationNotice.vue";
import OperatePasswordGate from "~/components/OperatePasswordGate.vue";
import password from "primevue/password";
import password from "~/components/PasswordField.vue";
import skeleton from "primevue/skeleton";
import { t as os_name_t } from "~/lib/os-name.js";
import { t as password_t } from "~/lib/password-2.js";
@@ -2048,6 +2048,7 @@ var xe = {
"onUpdate:modelValue": (n[27] ||= (e) =>
(unref(B).password = e)),
"input-id": `${unref(w)}-rescue-password`,
autocomplete: `new-password`,
feedback: !1,
toggleMask: ``,
class: `grow`,
@@ -31,7 +31,7 @@ import inputtext from "primevue/inputtext";
import select from "primevue/select";
import OperationNotice from "~/components/OperationNotice.vue";
import OperatePasswordGate from "~/components/OperatePasswordGate.vue";
import password from "primevue/password";
import password from "~/components/PasswordField.vue";
import skeleton from "primevue/skeleton";
import { n as renewal_quote_n } from "~/lib/renewal-quote.js";
import { r as renewal_quote_r } from "~/lib/renewal-quote.js";