From 67ec3c8aeb8a23dbd9668241c559b32418d1fd70 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 4 Oct 2026 21:49:39 +0800 Subject: [PATCH] fix(identity): align application modal with reference --- assets/css/routes/user-center/settings.css | 179 ++++++++++++++++-- assets/js/routes/user-center-auth.js | 154 +++++++++++++-- .../user-center-renderers/profile-auth.php | 44 +++-- 3 files changed, 333 insertions(+), 44 deletions(-) diff --git a/assets/css/routes/user-center/settings.css b/assets/css/routes/user-center/settings.css index ecf1c4d4..f5fc18d2 100644 --- a/assets/css/routes/user-center/settings.css +++ b/assets/css/routes/user-center/settings.css @@ -519,20 +519,171 @@ margin: 20px 0 0; } -.uc-auth-modal__dialog { width: min(620px, calc(100vw - 28px)); max-width: 620px; max-height: min(760px, calc(100vh - 40px)); overflow: auto; background: var(--surface-1, #fff); } -.aksrt-identity-modal-shell .aksrt-modal-runtime__dialog { width: min(620px, calc(100vw - 28px)); max-width: 620px; } -.aksrt-identity-modal-shell .uc-auth-modal__dialog { width: auto; max-width: none; max-height: none; overflow: visible; background: transparent; } -.uc-auth-modal__header > div { display: flex; align-items: center; gap: 10px; } -.uc-auth-modal__header > div > span { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 11px; background: var(--color-primary-soft); color: var(--color-primary); } -.uc-auth-modal__body { padding: 22px; } - -.uc-auth-form { display: grid; gap: 17px; } -.uc-auth-field { display: grid; gap: 7px; color: var(--color-ink-2); font-size: 13px; font-weight: 600; } -.uc-auth-form .form-control { width: 100%; min-height: 42px; padding: 9px 11px; border: 1px solid var(--color-line-soft, rgba(15, 23, 42, 0.1)); border-radius: 9px; background: var(--surface-1); color: var(--color-ink); font: inherit; font-weight: 400; box-sizing: border-box; } +.aksrt-identity-modal-shell .aksrt-modal-runtime__dialog { + width: min(600px, calc(100vw - 20px)); + max-width: 600px; + max-height: calc(100vh - 20px); + max-height: calc(100dvh - 20px); + overflow: auto; + border-radius: var(--main-radius, 8px); + background: var(--main-bg-color, var(--surface-1, #fff)); +} +.aksrt-identity-modal-shell .aksrt-modal-runtime__content { padding: 0; } +.uc-auth-modal { + position: relative; + overflow: hidden; + padding: 1px; + border-radius: var(--main-radius, 8px); + background: var(--main-bg-color, var(--surface-1, #fff)); +} +.uc-auth-modal__header, +.uc-auth-modal__header.colorful-bg { + position: absolute; + top: 1px; + right: 1px; + left: 1px; + display: flex; + align-items: center; + justify-content: center; + height: 120px; + overflow: hidden; + border-radius: var(--main-radius, 8px) var(--main-radius, 8px) 0 0; + background: var(--this-bg); + background-size: 120%; + color: var(--this-color); +} +.uc-auth-modal__header > .close { + position: absolute; + top: 10px; + right: 10px; + z-index: 9; + width: 30px; + height: 30px; + padding: 0; + border: 0; + background: transparent; + color: var(--this-color); + font-size: 18px; + line-height: 1; + opacity: .6; + cursor: pointer; +} +.uc-auth-modal__header > .close:hover { opacity: 1; } +.uc-auth-modal__header .colorful-make, +.uc-auth-modal__header .colorful-make::before, +.uc-auth-modal__header .colorful-make::after { + position: absolute; + border-radius: 100%; + background: var(--this-bg); + color: var(--this-color); + content: ''; +} +.uc-auth-modal__header .colorful-make { + top: 60%; + bottom: 0; + width: 323px; + height: 290px; + animation: uc-auth-colorful 120s linear infinite; +} +.uc-auth-modal__header .colorful-make::before { + top: -325px; + right: -558px; + bottom: 0; + width: 677px; + height: 300px; + transform: rotate(341deg); + opacity: .6; +} +.uc-auth-modal__header .colorful-make::after { + top: -170px; + bottom: 0; + left: -262px; + width: 402px; + height: 214px; + transform: rotate(326deg); + opacity: .4; +} +.uc-auth-modal__header .colorful-make + * { position: relative; } +.uc-auth-modal__body { + position: relative; + margin-top: 120px; + padding: 15px; + background: var(--main-bg-color, var(--surface-1, #fff)); +} +.uc-auth-form { margin: 0; } +.uc-auth-form__scroll { max-height: 50vh; max-height: 50dvh; } +.uc-auth-field { display: block; color: var(--muted-2-color); font-size: .9em; font-weight: 400; } +.uc-auth-field__label { display: block; margin-bottom: 6px; } +.uc-auth-form .form-control { width: 100%; box-sizing: border-box; } .uc-auth-form textarea.form-control { resize: vertical; } -.uc-auth-submit { display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 44px; } -.uc-auth-form__message { min-height: 18px; color: var(--color-error, #dc2626); font-size: 12px; text-align: center; } +.uc-auth-upload .preview .add { + appearance: none; + padding: 0; + border: 0; + cursor: pointer; +} +.uc-auth-upload .preview-item { overflow: hidden; } +.uc-auth-upload .preview-item img { object-fit: contain; } +.uc-auth-preview-remove { + position: absolute; + top: 5px; + right: 5px; + display: grid; + place-items: center; + width: 24px; + height: 24px; + padding: 0; + border: 0; + border-radius: 50%; + background: #fd2c53; + color: #fff; + font-size: 20px; + line-height: 1; + cursor: pointer; +} +.aksrt-identity-modal-shell .uc-auth-modal .uc-auth-modal__body .uc-auth-modal__footer { + display: flex; + grid-template-columns: none; + gap: 0; + margin: 0 -15px -15px; + overflow: hidden; + border-top: 1px solid var(--main-border-color, var(--color-line, rgba(50, 50, 50, .12))); + border-radius: 0 0 var(--main-radius, 8px) var(--main-radius, 8px); +} +.aksrt-identity-modal-shell .uc-auth-modal .uc-auth-modal__body .uc-auth-modal__footer > .but { + display: flex; + flex: 1 1 50%; + align-items: center; + justify-content: center; + min-height: 48px; + margin: 0 !important; + padding: .8em; + border: 0; + border-radius: 0; + background: transparent; + color: var(--muted-color, #888); + font: inherit; + font-weight: 400; + cursor: pointer; +} +.aksrt-identity-modal-shell .uc-auth-modal .uc-auth-modal__body .uc-auth-modal__footer > .but + .but { + border-left: 1px solid var(--main-border-color, var(--color-line, rgba(50, 50, 50, .12))); +} +.aksrt-identity-modal-shell .uc-auth-modal .uc-auth-modal__body .uc-auth-modal__footer > .but.c-blue { + background: transparent; + color: var(--this-color, #2997f7); +} +.aksrt-identity-modal-shell .uc-auth-modal .uc-auth-modal__body .uc-auth-modal__footer > .but:hover { + background: var(--this-bg, rgba(136, 136, 136, .1)); +} +.aksrt-identity-modal-shell .uc-auth-modal .uc-auth-modal__body .uc-auth-modal__footer > .but:disabled { opacity: .5; cursor: wait; } +@keyframes uc-auth-colorful { to { transform: rotate(360deg); } } -@media (max-width: 640px) { - .uc-auth-modal__dialog { position: fixed; inset: auto 0 0; width: 100%; max-width: none; max-height: 88vh; margin: 0; border-radius: 18px 18px 0 0; } +@media (max-width: 768px) { + .aksrt-identity-modal-shell.aksrt-modal-runtime--bottom .aksrt-modal-runtime__dialog { + width: calc(100% - 20px); + max-width: none; + max-height: calc(100dvh - 30px - env(safe-area-inset-bottom)); + margin: 0 10px calc(10px + env(safe-area-inset-bottom)); + } } diff --git a/assets/js/routes/user-center-auth.js b/assets/js/routes/user-center-auth.js index 50084a7c..b5d2c475 100644 --- a/assets/js/routes/user-center-auth.js +++ b/assets/js/routes/user-center-auth.js @@ -15,6 +15,10 @@ return error && error.message ? error.message : fallback; } + function toast(message, type, time, id) { + if (message && typeof window.notyf === 'function') window.notyf(message, type || 'success', time, id); + } + function init(root) { if (!root || root.dataset.identityBound === '1') return; root.dataset.identityBound = '1'; @@ -27,39 +31,154 @@ function open() { runtime.open({ html: template.innerHTML, + height: 320, kind: 'identity', className: 'aksrt-identity-modal-shell', mobileBottom: true, onContent: function (_, session) { var shell = session && session.shell; var form = shell && shell.querySelector('[data-auth-create]'); - if (!form) return; - form.addEventListener('submit', function (event) { - event.preventDefault(); - var input = form.querySelector('[name="evidence[]"]'); - var message = form.querySelector('[data-auth-message]'); - var button = form.querySelector('[type="submit"]'); - var files = input ? Array.prototype.slice.call(input.files || []) : []; - if (files.length > 3) { - if (message) message.textContent = '证明材料最多上传 3 张'; + var input = form && form.querySelector('[data-auth-evidence]'); + var preview = form && form.querySelector('[data-auth-preview]'); + if (!form || !input || !preview) return; + shell.setAttribute('aria-labelledby', 'uc-auth-modal-title'); + + var selectedFiles = []; + function renderPreview() { + preview.replaceChildren(); + selectedFiles.forEach(function (entry, index) { + var item = document.createElement('div'); + item.className = 'preview-item'; + var image = document.createElement('img'); + image.className = 'fit-cover'; + image.src = entry.url; + image.alt = entry.file.name; + var remove = document.createElement('button'); + remove.type = 'button'; + remove.className = 'uc-auth-preview-remove'; + remove.dataset.authRemove = String(index); + remove.setAttribute('aria-label', '移除 ' + entry.file.name); + remove.textContent = '×'; + item.appendChild(image); + item.appendChild(remove); + preview.appendChild(item); + }); + + if (selectedFiles.length < 3) { + var add = document.createElement('button'); + add.type = 'button'; + add.className = 'add'; + add.dataset.authFilePicker = ''; + add.setAttribute('aria-label', '选择证明材料图片'); + preview.appendChild(add); + } + } + + function releasePreviewUrls() { + selectedFiles.forEach(function (entry) { window.URL.revokeObjectURL(entry.url); }); + selectedFiles = []; + } + + function onModalClose(event) { + if (!event.detail || event.detail.shell !== shell) return; + document.removeEventListener('aksrt:modal:close', onModalClose); + releasePreviewUrls(); + } + + document.addEventListener('aksrt:modal:close', onModalClose); + preview.addEventListener('click', function (event) { + var remove = event.target.closest('[data-auth-remove]'); + if (remove) { + var index = Number(remove.dataset.authRemove); + var entry = selectedFiles[index]; + if (entry) window.URL.revokeObjectURL(entry.url); + selectedFiles.splice(index, 1); + renderPreview(); + return; + } + if (event.target.closest('[data-auth-file-picker]')) input.click(); + }); + input.addEventListener('change', function () { + var files = Array.prototype.slice.call(input.files || []); + input.value = ''; + if (!files.length) return; + + var unsupported = files.some(function (file) { return !/^image\/(gif|jpe?g|png)$/i.test(file.type); }); + if (unsupported) { + toast('证明材料仅支持 GIF、JPG、PNG 图片', 'danger'); return; } if (files.some(function (file) { return file.size > 2 * 1024 * 1024; })) { - if (message) message.textContent = '单张证明材料不能超过 2MB'; + toast('单张证明材料不能超过 2MB', 'danger'); return; } - if (button) button.disabled = true; - if (message) message.textContent = ''; + var uniqueFiles = files.filter(function (file, index) { + var key = [file.name, file.size, file.lastModified].join(':'); + return !selectedFiles.some(function (entry) { return entry.key === key; }) && + files.findIndex(function (candidate) { + return [candidate.name, candidate.size, candidate.lastModified].join(':') === key; + }) === index; + }); + if (selectedFiles.length + uniqueFiles.length > 3) { + toast('证明材料最多上传 3 张', 'danger'); + return; + } + + uniqueFiles.forEach(function (file) { + selectedFiles.push({ + file: file, + key: [file.name, file.size, file.lastModified].join(':'), + url: window.URL.createObjectURL(file), + }); + }); + renderPreview(); + }); + + var header = shell.querySelector('.uc-auth-modal__header'); + if (header) { + var touchStartY = null; + header.addEventListener('touchstart', function (event) { + touchStartY = event.touches.length ? event.touches[0].clientY : null; + }, { passive: true }); + header.addEventListener('touchend', function (event) { + var touchEndY = event.changedTouches.length ? event.changedTouches[0].clientY : null; + if (touchStartY !== null && touchEndY !== null && touchEndY - touchStartY > 60 && + runtime.getActive && runtime.getActive() === shell) { + runtime.close('swipe'); + } + touchStartY = null; + }, { passive: true }); + } + + form.addEventListener('submit', function (event) { + event.preventDefault(); + var invalidField = form.querySelector(':invalid'); + if (!form.checkValidity()) { + toast((invalidField && invalidField.validationMessage) || '请检查申请信息是否填写完整', 'danger'); + if (invalidField && typeof invalidField.focus === 'function') invalidField.focus(); + return; + } + var button = form.querySelector('[type="submit"]'); + var formData = new FormData(form); + selectedFiles.forEach(function (entry) { + formData.append('evidence[]', entry.file, entry.file.name); + }); + var submitLabel = button ? button.innerHTML : ''; + if (button) { + button.disabled = true; + button.innerHTML = '正在提交…'; + } + toast('正在提交申请…', 'load', '', 'aksrt_identity_submit'); window.AksrtRequest.request(config.restBase, { method: 'POST', credentials: 'same-origin', headers: { 'X-WP-Nonce': config.nonce || '' }, - body: new FormData(form), + body: formData, }).then(function (response) { var result = parseResponse(response); if (!result || result.success === false) throw new Error((result && result.message) || '申请提交失败,请稍后重试'); - if (message) message.textContent = '申请已提交,请耐心等待管理员审核。'; + toast('申请已提交,请耐心等待管理员审核。', 'success', '', 'aksrt_identity_submit'); var summary = root.querySelector('[data-auth-summary]'); if (summary) { summary.replaceChildren(); @@ -101,9 +220,12 @@ } if (runtime.getActive && runtime.getActive() === shell) runtime.close('submit'); }).catch(function (error) { - if (message) message.textContent = errorMessage(error, '申请提交失败,请稍后重试'); + toast(errorMessage(error, '申请提交失败,请稍后重试'), 'danger', '', 'aksrt_identity_submit'); }).finally(function () { - if (button) button.disabled = false; + if (button) { + button.disabled = false; + button.innerHTML = submitLabel; + } }); }); }, diff --git a/inc/helpers/user-center-renderers/profile-auth.php b/inc/helpers/user-center-renderers/profile-auth.php index 3620ebd5..02bf6c15 100644 --- a/inc/helpers/user-center-renderers/profile-auth.php +++ b/inc/helpers/user-center-renderers/profile-auth.php @@ -14,7 +14,8 @@ $info = is_array($snapshot['info'] ?? null) ? $snapshot['info'] : []; $application = is_array($snapshot['application'] ?? null) ? $snapshot['application'] : []; $status = (string) ($snapshot['status'] ?? ''); $can_apply = !empty($snapshot['can_apply']); -$auth_icon = ''; +$auth_icon = function_exists('aksrt_icon_markup') ? aksrt_icon_markup('zibsvg-user-auth') : ''; +$close_icon = function_exists('aksrt_icon_markup') ? aksrt_icon_markup('zibsvg-close', 'ic-close') : ''; $config = [ 'restBase' => untrailingslashit(rest_url('aksrt/v1/identity/applications')), 'nonce' => wp_create_nonce('wp_rest'), @@ -53,20 +54,35 @@ $config = [