update user invite style
This commit is contained in:
@@ -32,7 +32,7 @@ export type InviteRegisterData = {
|
||||
};
|
||||
|
||||
export type InviteRegisterRequest = {
|
||||
captchaId: string;
|
||||
detailId: string;
|
||||
captcha: string;
|
||||
data: InviteRegisterData;
|
||||
};
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<div class="register-page">
|
||||
<section v-if="isLoading" class="register-panel"><t-loading text="正在校验邀请链接" /></section>
|
||||
<section v-if="isLoading" class="register-panel"><t-loading text="正在校验邀请码" /></section>
|
||||
<section v-else-if="!isValid" class="register-panel invalid">
|
||||
<div class="brand">
|
||||
<div class="logo"><span>糕</span></div>
|
||||
<logo />
|
||||
<h1>糕店客户管理</h1>
|
||||
</div>
|
||||
<h2>邀请链接已失效</h2>
|
||||
<p v-text="invalidMessage" />
|
||||
<h2>邀请码已失效</h2>
|
||||
<p class="tips gray" v-text="invalidMessage" />
|
||||
</section>
|
||||
<section v-else class="register-panel">
|
||||
<div class="brand">
|
||||
<div class="logo"><span>糕</span></div>
|
||||
<h1>加入糕店客户管理</h1>
|
||||
<logo />
|
||||
<h1>加入糕店客户管理系统</h1>
|
||||
</div>
|
||||
<p class="expire" v-text="expireText" />
|
||||
<t-form class="form" label-align="left">
|
||||
<p class="tips gray" v-text="expireText" />
|
||||
<t-form class="form" label-align="left" label-width="6rem">
|
||||
<t-form-item label="手机号">
|
||||
<t-input v-model.trim="formModel.phoneNo" type="tel" inputmode="tel" borderless placeholder="请输入手机号" />
|
||||
</t-form-item>
|
||||
@@ -28,23 +28,38 @@
|
||||
<t-form-item label="确认密码">
|
||||
<t-input v-model="formModel.confirmPassword" type="password" borderless placeholder="请再次输入登录密码" />
|
||||
</t-form-item>
|
||||
<t-form-item label="验证码">
|
||||
<div class="captcha-row">
|
||||
<t-form-item label="图形验证码">
|
||||
<t-input v-model.trim="formModel.captcha" borderless placeholder="请输入验证码" />
|
||||
<captcha ref="captchaRef" class="captcha" @error="handleCaptchaError" />
|
||||
</div>
|
||||
</t-form-item>
|
||||
<div class="captcha-wrapper">
|
||||
<captcha ref="captchaRef" class="captcha" :width="120" :height="40" @error="handleCaptchaError" />
|
||||
</div>
|
||||
<t-form-item label="短信验证码">
|
||||
<t-input v-model.trim="formModel.smsCode" borderless placeholder="请输入短信验证码" />
|
||||
</t-form-item>
|
||||
<div class="send-wrapper">
|
||||
<t-button
|
||||
class="send-sms"
|
||||
type="button"
|
||||
size="extra-small"
|
||||
theme="primary"
|
||||
:content="sendSmsText"
|
||||
:disabled="!canSendSms"
|
||||
@click="sendSms"
|
||||
/>
|
||||
</div>
|
||||
</t-form>
|
||||
<t-button class="submit" theme="primary" size="medium" :loading="isSubmitting" :disabled="isExpired" @click="register">注册并登录</t-button>
|
||||
<t-button class="submit" theme="primary" size="medium" :loading="isSubmitting" :disabled="isExpired || isSubmitting" @click="register">注册并登录</t-button>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Toast } from "tdesign-mobile-vue";
|
||||
import { Captcha, useUserStore, type LoginResponse, Time } from "timi-web";
|
||||
import { Captcha, NotifyAPI, useUserStore, type LoginResponse, Time } from "timi-web";
|
||||
import ManagedUserAPI from "@/api/ManagedUserAPI";
|
||||
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||
import Logo from "@/pages/user/component/Logo.vue";
|
||||
|
||||
type CaptchaExpose = { captchaId: string; update: () => Promise<void> };
|
||||
|
||||
@@ -57,17 +72,43 @@ const code = String(route.query.code || "");
|
||||
const isLoading = ref(true);
|
||||
const isValid = ref(false);
|
||||
const expireAt = ref(0);
|
||||
const invalidMessage = ref("邀请链接无效或已过期,请联系邀请人重新生成。");
|
||||
const invalidMessage = ref("邀请码无效或已过期,请联系邀请人重新生成。");
|
||||
const isSubmitting = ref(false);
|
||||
const captchaRef = ref<CaptchaExpose | null>(null);
|
||||
const formModel = reactive({ phoneNo: "", name: "", password: "", confirmPassword: "", captcha: "" });
|
||||
const formModel = reactive({
|
||||
phoneNo: "",
|
||||
name: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
captcha: "",
|
||||
smsCode: "",
|
||||
notifyDetailId: ""
|
||||
});
|
||||
const currentTime = ref(Date.now());
|
||||
const isExpired = computed(() => !expireAt.value || currentTime.value >= expireAt.value);
|
||||
const expireText = computed(() => isExpired.value ? "邀请链接已失效" : `有效期至 ${Time.toDateTime(expireAt.value)}`);
|
||||
const expireText = computed(() => isExpired.value ? "邀请码已失效" : `有效期至 ${Time.toDateTime(expireAt.value)}`);
|
||||
const sendSmsCountdown = ref(0);
|
||||
let sendSmsTimer: number | undefined;
|
||||
const isSendingSms = computed(() => 0 < sendSmsCountdown.value);
|
||||
const sendSmsText = computed(() => isSendingSms.value ? `${sendSmsCountdown.value} 秒后重新发送` : "发送验证码");
|
||||
const canSendSms = computed(() => (
|
||||
!isExpired.value
|
||||
&& !isSendingSms.value
|
||||
&& /^1\d{10}$/.test(formModel.phoneNo)
|
||||
&& 4 === formModel.captcha.length
|
||||
));
|
||||
|
||||
watch(() => formModel.phoneNo, () => {
|
||||
if (!formModel.notifyDetailId) {
|
||||
return;
|
||||
}
|
||||
formModel.notifyDetailId = "";
|
||||
formModel.smsCode = "";
|
||||
});
|
||||
|
||||
async function validateInvite(): Promise<void> {
|
||||
if (!/^\w{10}$/.test(code)) {
|
||||
invalidMessage.value = "邀请链接格式不正确。";
|
||||
invalidMessage.value = "邀请码格式不正确。";
|
||||
return;
|
||||
}
|
||||
const result = await ManagedUserAPI.validateInvite(code);
|
||||
@@ -92,39 +133,89 @@ function validateForm(): boolean {
|
||||
Toast.warning("两次输入的密码不一致");
|
||||
return false;
|
||||
}
|
||||
if (!captchaRef.value?.captchaId || !formModel.captcha) {
|
||||
Toast.warning("请输入图形验证码");
|
||||
if (!formModel.notifyDetailId || !/^\d{6}$/.test(formModel.smsCode)) {
|
||||
Toast.warning("请先获取并输入短信验证码");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getCaptchaId(): string {
|
||||
return captchaRef.value?.captchaId || "";
|
||||
}
|
||||
|
||||
function clearSmsTimer(): void {
|
||||
if (sendSmsTimer === undefined) {
|
||||
return;
|
||||
}
|
||||
window.clearInterval(sendSmsTimer);
|
||||
sendSmsTimer = undefined;
|
||||
}
|
||||
|
||||
function startSmsTimer(): void {
|
||||
clearSmsTimer();
|
||||
sendSmsCountdown.value = 60;
|
||||
sendSmsTimer = window.setInterval(() => {
|
||||
sendSmsCountdown.value -= 1;
|
||||
if (!(0 < sendSmsCountdown.value)) {
|
||||
clearSmsTimer();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
async function sendSms(): Promise<void> {
|
||||
if (!canSendSms.value) {
|
||||
return;
|
||||
}
|
||||
const captchaId = getCaptchaId();
|
||||
if (!captchaId) {
|
||||
Toast.warning("图形验证码尚未加载,请稍后重试");
|
||||
await captchaRef.value?.update();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
formModel.notifyDetailId = await NotifyAPI.smsSend({
|
||||
captchaId,
|
||||
captcha: formModel.captcha,
|
||||
data: {
|
||||
detailList: [{ sendTo: formModel.phoneNo }]
|
||||
}
|
||||
});
|
||||
startSmsTimer();
|
||||
Toast.success(`验证码已发送,请检查 ${formModel.phoneNo} 手机短信(易保网络)`);
|
||||
} catch {
|
||||
formModel.notifyDetailId = "";
|
||||
} finally {
|
||||
formModel.captcha = "";
|
||||
await captchaRef.value?.update();
|
||||
}
|
||||
}
|
||||
|
||||
async function register(): Promise<void> {
|
||||
if (isSubmitting.value || isExpired.value || !validateForm()) {
|
||||
if (isExpired.value) {
|
||||
Toast.warning("邀请链接已失效,请联系邀请人重新生成");
|
||||
Toast.warning("邀请码已失效,请联系邀请人重新生成");
|
||||
}
|
||||
return;
|
||||
}
|
||||
isSubmitting.value = true;
|
||||
try {
|
||||
const result = await ManagedUserAPI.registerByInvite({
|
||||
captchaId: captchaRef.value?.captchaId || "",
|
||||
captcha: formModel.captcha,
|
||||
data: {
|
||||
code,
|
||||
phoneNo: formModel.phoneNo,
|
||||
name: formModel.name,
|
||||
password: formModel.password
|
||||
}
|
||||
},
|
||||
detailId: formModel.notifyDetailId,
|
||||
captcha: formModel.smsCode
|
||||
});
|
||||
await userStore.updateLoginUser(result as LoginResponse);
|
||||
await currentStoreStore.loadAfterLogin();
|
||||
Toast.success("注册成功");
|
||||
await router.replace("/management");
|
||||
} catch {
|
||||
formModel.captcha = "";
|
||||
await captchaRef.value?.update();
|
||||
formModel.smsCode = "";
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
@@ -143,7 +234,7 @@ onMounted(async () => {
|
||||
try {
|
||||
await validateInvite();
|
||||
} catch {
|
||||
invalidMessage.value = "邀请链接校验失败,请稍后重试。";
|
||||
invalidMessage.value = "邀请码校验失败,请稍后重试。";
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
@@ -151,6 +242,7 @@ onMounted(async () => {
|
||||
|
||||
onUnmounted(() => {
|
||||
window.clearInterval(expireTimer);
|
||||
clearSmsTimer();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -175,6 +267,14 @@ onUnmounted(() => {
|
||||
border-radius: 1.4rem;
|
||||
flex-direction: column;
|
||||
|
||||
&.invalid {
|
||||
text-align: center;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -187,62 +287,30 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
color: #FFF;
|
||||
display: grid;
|
||||
background: #0f766e;
|
||||
box-shadow: 0 .75rem 1.5rem rgba(15, 118, 110, .22);
|
||||
place-items: center;
|
||||
border-radius: 1.25rem;
|
||||
|
||||
span {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
.expire,
|
||||
p,
|
||||
h2 {
|
||||
.tips {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.expire,
|
||||
p {
|
||||
color: #64748b;
|
||||
font-size: .875rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #0f172a;
|
||||
font-size: 1.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form {
|
||||
margin: 0;
|
||||
padding: .25rem 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.captcha-row {
|
||||
gap: .75rem;
|
||||
width: 100%;
|
||||
.send-wrapper,
|
||||
.captcha-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: .5rem;
|
||||
justify-content: end;
|
||||
|
||||
.t-input {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.tui-captcha) {
|
||||
flex: 0 0 7.5rem;
|
||||
.captcha {
|
||||
border: 1px solid var(--td-border-level-1-color);
|
||||
}
|
||||
}
|
||||
|
||||
.send-sms {
|
||||
flex: 0 0 7.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.submit {
|
||||
gap: .5rem;
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="login-page">
|
||||
<section class="login-panel">
|
||||
<div class="brand">
|
||||
<div class="logo"><span>糕</span></div>
|
||||
<logo />
|
||||
<h1>糕店客户管理</h1>
|
||||
</div>
|
||||
<t-segmented class="login-type" v-model="loginType" :options="loginTypeOptions" />
|
||||
@@ -15,6 +15,7 @@
|
||||
<script setup lang="ts">
|
||||
import PasswordLogin from "@/pages/user/component/PasswordLogin.vue";
|
||||
import SmsLogin from "@/pages/user/component/SmsLogin.vue";
|
||||
import Logo from "@/pages/user/component/Logo.vue";
|
||||
|
||||
defineOptions({
|
||||
name: "LoginIndex"
|
||||
@@ -74,22 +75,6 @@ const loginTypeOptions = [
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
color: #FFF;
|
||||
display: grid;
|
||||
background: #0f766e;
|
||||
box-shadow: 0 .75rem 1.5rem rgba(15, 118, 110, .22);
|
||||
place-items: center;
|
||||
border-radius: 1.25rem;
|
||||
|
||||
span {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-dark) {
|
||||
|
||||
@@ -5,8 +5,20 @@
|
||||
<div v-else class="empty" v-text="message" />
|
||||
</section>
|
||||
<div v-if="posterImage" class="actions">
|
||||
<t-button class="bg-white" variant="outline" theme="primary" block @click="copyLink">复制链接</t-button>
|
||||
<t-button theme="primary" block :loading="isDownloading" @click="savePoster">保存海报</t-button>
|
||||
<t-button
|
||||
class="button bg-white"
|
||||
variant="outline"
|
||||
theme="primary"
|
||||
block
|
||||
@click="copyLink"
|
||||
>复制链接</t-button>
|
||||
<t-button
|
||||
class="button"
|
||||
theme="primary"
|
||||
block
|
||||
:loading="isDownloading"
|
||||
@click="savePoster"
|
||||
>保存海报</t-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -105,13 +117,11 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.actions {
|
||||
--td-button-border-radius: 1rem;
|
||||
|
||||
gap: .75rem;
|
||||
left: 1rem;
|
||||
right: 1rem;
|
||||
bottom: calc(.75rem + env(safe-area-inset-bottom, 0));
|
||||
display: grid;
|
||||
position: fixed;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
<t-cell title="修改密码" arrow hover @click="toPassword" />
|
||||
</t-cell-group>
|
||||
<t-button
|
||||
class="logout bg-white"
|
||||
theme="danger"
|
||||
class="red bg-white cell-button"
|
||||
variant="text"
|
||||
block
|
||||
@click="confirmLogout"
|
||||
variant="outline"
|
||||
>退出登录</t-button>
|
||||
<input
|
||||
ref="avatarInputRef"
|
||||
@@ -252,11 +252,6 @@ onUnmounted(() => {
|
||||
color: var(--td-text-color-primary);
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.logout {
|
||||
margin: 1rem auto;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-dark) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="logo">
|
||||
<span>糕</span>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.logo {
|
||||
color: #FFF;
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
display: grid;
|
||||
background: #0f766e;
|
||||
box-shadow: 0 .75rem 1.5rem rgba(15, 118, 110, .22);
|
||||
place-items: center;
|
||||
border-radius: 1.25rem;
|
||||
|
||||
span {
|
||||
font-size: 2rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -30,7 +30,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Toast } from "tdesign-mobile-vue";
|
||||
import { Message } from "@/utils/message";
|
||||
import { Captcha, NotifyAPI, UserAPI, useUserStore } from "timi-web";
|
||||
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||
|
||||
@@ -107,7 +107,7 @@ async function sendSms(): Promise<void> {
|
||||
|
||||
const captchaId = getCaptchaId();
|
||||
if (!captchaId) {
|
||||
Toast.warning("图形验证码尚未加载,请稍后重试");
|
||||
Message.warning("图形验证码尚未加载,请稍后重试");
|
||||
await captchaRef.value?.update();
|
||||
return;
|
||||
}
|
||||
@@ -125,7 +125,7 @@ async function sendSms(): Promise<void> {
|
||||
}
|
||||
});
|
||||
startSmsTimer();
|
||||
Toast.success(`验证码已发送,请检查 ${formModel.mobilePhone} 手机短信`);
|
||||
Message.success(`验证码已发送,请检查 ${formModel.mobilePhone} 手机短信(易保网络)`, 5000);
|
||||
} catch {
|
||||
formModel.notifyDetailId = "";
|
||||
} finally {
|
||||
@@ -136,11 +136,11 @@ async function sendSms(): Promise<void> {
|
||||
|
||||
function validateForm(): boolean {
|
||||
if (!/^1\d{10}$/.test(formModel.mobilePhone)) {
|
||||
Toast.warning("请输入正确的 11 位手机号");
|
||||
Message.warning("请输入正确的 11 位手机号");
|
||||
return false;
|
||||
}
|
||||
if (!formModel.notifyDetailId || !formModel.smsCode) {
|
||||
Toast.warning("请先获取并输入短信验证码");
|
||||
Message.warning("请先获取并输入短信验证码");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -14,14 +14,6 @@
|
||||
<button class="close" type="button" @click="popupVisible = false">×</button>
|
||||
</header>
|
||||
<t-form class="form" label-align="left">
|
||||
<t-form-item
|
||||
v-if="isGlobalManager"
|
||||
label="目标门店"
|
||||
arrow
|
||||
@click="openPicker('store')"
|
||||
>
|
||||
<span v-text="selectedStoreName" />
|
||||
</t-form-item>
|
||||
<t-form-item
|
||||
label="目标角色"
|
||||
arrow
|
||||
@@ -29,6 +21,14 @@
|
||||
>
|
||||
<span v-text="selectedRoleName" />
|
||||
</t-form-item>
|
||||
<t-form-item
|
||||
v-if="shouldSelectStore"
|
||||
label="目标门店"
|
||||
arrow
|
||||
@click="openPicker('store')"
|
||||
>
|
||||
<span v-text="selectedStoreName" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</section>
|
||||
</t-dialog>
|
||||
@@ -47,8 +47,8 @@
|
||||
import { Toast } from "tdesign-mobile-vue";
|
||||
import ManagedUserAPI from "@/api/ManagedUserAPI";
|
||||
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||
import { gaoRoleCodes } from "@/utils/permission";
|
||||
import { RoleAPI, useUserStore } from "timi-web";
|
||||
import { coreRoleCodes, gaoRoleCodes } from "@/utils/permission";
|
||||
import { RoleAPI, type Role, useUserStore } from "timi-web";
|
||||
|
||||
defineOptions({ name: "UserInvite" });
|
||||
|
||||
@@ -62,17 +62,20 @@ const emit = defineEmits<{
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const currentStoreStore = useCurrentStoreStore();
|
||||
const isGlobalManager = computed(() => userStore.hasRoles(gaoRoleCodes.globalManager));
|
||||
const hasGlobalStoreScope = computed(() => userStore.hasRoles([coreRoleCodes.admin, gaoRoleCodes.globalManager], "or"));
|
||||
const popupVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: value => emit("update:visible", value)
|
||||
});
|
||||
const formModel = reactive({ roleId: "", storeId: "" });
|
||||
const roleList = ref<Array<{ id?: string; name?: string; code?: string }>>([]);
|
||||
const roleList = ref<Role[]>([]);
|
||||
const isGenerating = ref(false);
|
||||
const isLoaded = ref(false);
|
||||
const roleOptions = computed(() => roleList.value.filter(item => item.id).map(item => ({ label: item.name || item.code || "未命名角色", value: item.id as string })));
|
||||
const storeOptions = computed(() => currentStoreStore.storeList.filter(item => item.id).map(item => ({ label: item.name || item.code || "未命名门店", value: item.id as string })));
|
||||
const selectedRole = computed(() => roleList.value.find(item => item.id === formModel.roleId));
|
||||
const isGlobalManagerRole = computed(() => selectedRole.value?.code === "GLOBAL_MANAGER");
|
||||
const shouldSelectStore = computed(() => hasGlobalStoreScope.value && !!selectedRole.value && !isGlobalManagerRole.value);
|
||||
const selectedRoleName = computed(() => roleOptions.value.find(item => item.value === formModel.roleId)?.label || "请选择角色");
|
||||
const selectedStoreName = computed(() => storeOptions.value.find(item => item.value === formModel.storeId)?.label || "请选择门店");
|
||||
const pickerVisible = ref(false);
|
||||
@@ -85,14 +88,14 @@ async function loadData(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
const roleRequest = RoleAPI.manageableList("GAO");
|
||||
if (isGlobalManager.value) {
|
||||
if (hasGlobalStoreScope.value) {
|
||||
await currentStoreStore.loadStoreList();
|
||||
formModel.storeId = currentStoreStore.storeList.some(item => item.id === props.storeId)
|
||||
? props.storeId || ""
|
||||
: currentStoreStore.currentStoreId;
|
||||
}
|
||||
roleList.value = await roleRequest;
|
||||
if (!isGlobalManager.value) {
|
||||
if (!hasGlobalStoreScope.value) {
|
||||
const store = await currentStoreStore.loadLoginStore();
|
||||
formModel.storeId = store?.id || "";
|
||||
}
|
||||
@@ -109,6 +112,9 @@ function confirmPicker(): void {
|
||||
const value = String(pickerValue.value[0] || "");
|
||||
if (pickerType.value === "role") {
|
||||
formModel.roleId = value;
|
||||
if (isGlobalManagerRole.value) {
|
||||
formModel.storeId = "";
|
||||
}
|
||||
} else {
|
||||
formModel.storeId = value;
|
||||
}
|
||||
@@ -116,20 +122,20 @@ function confirmPicker(): void {
|
||||
}
|
||||
|
||||
async function generateInvite(): Promise<void> {
|
||||
if (!formModel.roleId || (isGlobalManager.value && !formModel.storeId)) {
|
||||
Toast.warning(isGlobalManager.value ? "请选择角色和门店" : "请选择角色");
|
||||
if (!formModel.roleId || (shouldSelectStore.value && !formModel.storeId)) {
|
||||
Toast.warning(shouldSelectStore.value ? "请选择角色和门店" : "请选择角色");
|
||||
return;
|
||||
}
|
||||
isGenerating.value = true;
|
||||
try {
|
||||
const result = await ManagedUserAPI.invite(isGlobalManager.value ? { roleId: formModel.roleId, storeId: formModel.storeId } : { roleId: formModel.roleId });
|
||||
const result = await ManagedUserAPI.invite(shouldSelectStore.value ? { roleId: formModel.roleId, storeId: formModel.storeId } : { roleId: formModel.roleId });
|
||||
popupVisible.value = false;
|
||||
await router.push({
|
||||
name: "UserInvitePoster",
|
||||
query: {
|
||||
code: result.code,
|
||||
expireAt: String(result.expireAt),
|
||||
storeName: isGlobalManager.value ? selectedStoreName.value : currentStoreStore.currentStoreName,
|
||||
storeName: isGlobalManagerRole.value ? "糕店客户管理系统" : shouldSelectStore.value ? selectedStoreName.value : currentStoreStore.currentStoreName,
|
||||
roleName: selectedRoleName.value,
|
||||
inviterName: getCurrentUserName()
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { createRouter, createWebHistory, type RouteLocationNormalized } from "vu
|
||||
import RootLayout from "@/layout/RootLayout.vue";
|
||||
import MainLayout from "@/layout/MainLayout.vue";
|
||||
import Login from "@/pages/user/LoginIndex.vue";
|
||||
import InviteRegister from "@/pages/InviteRegister.vue";
|
||||
import InviteRegister from "@/pages/user/InviteRegister.vue";
|
||||
import chartRoutes from "@/router/routes/chartRoutes";
|
||||
import customerRoutes from "@/router/routes/customerRoutes";
|
||||
import { managementAccountRoutes, managementSettingRoutes, storeRoutes } from "@/router/routes/managementRoutes";
|
||||
|
||||
@@ -38,6 +38,17 @@ html.theme-dark body {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cell-button {
|
||||
--td-button-border-radius: 1rem;
|
||||
|
||||
width: calc(100% - 2rem);
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.t-input--password {
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.t-form__label {
|
||||
color: #555;
|
||||
flex: none;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { MessagePlugin as TDesignMessage } from "tdesign-mobile-vue";
|
||||
import type { MessagePluginOptions } from "tdesign-mobile-vue/es/message/message";
|
||||
|
||||
/** Message 支持的调用参数。 */
|
||||
export type MessageOptions = MessagePluginOptions;
|
||||
export type MessageInput = string | MessageOptions;
|
||||
|
||||
type MessageTheme = Exclude<NonNullable<MessageOptions["theme"]>, "loading">;
|
||||
|
||||
interface TDesignMessageApi {
|
||||
info: (options: MessageOptions) => void;
|
||||
success: (options: MessageOptions) => void;
|
||||
warning: (options: MessageOptions) => void;
|
||||
error: (options: MessageOptions) => void;
|
||||
closeAll: () => void;
|
||||
}
|
||||
|
||||
const tDesignMessage = TDesignMessage as unknown as TDesignMessageApi;
|
||||
|
||||
function normalizeOptions(input: MessageInput): MessageOptions {
|
||||
return typeof input === "string" ? { content: input } : input;
|
||||
}
|
||||
|
||||
function normalizeOptionsWithDuration(input: MessageInput, duration?: number): MessageOptions {
|
||||
const options = normalizeOptions(input);
|
||||
return duration === undefined ? options : { ...options, duration };
|
||||
}
|
||||
|
||||
function showMessage(input: MessageInput, duration?: number): void {
|
||||
const options = normalizeOptionsWithDuration(input, duration);
|
||||
const theme = options.theme || "info";
|
||||
tDesignMessage[theme](options);
|
||||
}
|
||||
|
||||
function showMessageByTheme(theme: MessageTheme, input: MessageInput, duration?: number): void {
|
||||
tDesignMessage[theme]({
|
||||
offset: [10, 16],
|
||||
...normalizeOptionsWithDuration(input, duration),
|
||||
theme
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一 Message 调用方式,使其支持类似 Toast 的字符串和配置对象调用。
|
||||
*/
|
||||
export const Message = Object.assign(showMessage, {
|
||||
info: (input: MessageInput, duration?: number): void => showMessageByTheme("info", input, duration),
|
||||
success: (input: MessageInput, duration?: number): void => showMessageByTheme("success", input, duration),
|
||||
warning: (input: MessageInput, duration?: number): void => showMessageByTheme("warning", input, duration),
|
||||
error: (input: MessageInput, duration?: number): void => showMessageByTheme("error", input, duration),
|
||||
closeAll: (): void => tDesignMessage.closeAll()
|
||||
});
|
||||
+2
-2
@@ -209,7 +209,7 @@ function renderInvitePoster(options: InvitePosterOptions): HTMLCanvasElement {
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
context.fillStyle = teal;
|
||||
context.fillRect(0, 0, canvas.width, 640);
|
||||
context.fillStyle = "#CCFBF1";
|
||||
context.fillStyle = "#6eb0a5";
|
||||
context.beginPath();
|
||||
context.arc(1140, 40, 220, 0, Math.PI * 2);
|
||||
context.fill();
|
||||
@@ -250,7 +250,7 @@ function renderInvitePoster(options: InvitePosterOptions): HTMLCanvasElement {
|
||||
context.fillText("客户档案 | 事件登记 | 积分激励 | 门店协同 | AI 分析", 600, 1645);
|
||||
context.fillStyle = "#94A3B8";
|
||||
context.font = "400 40px Microsoft YaHei, sans-serif";
|
||||
context.fillText("本邀请仅限本人使用,请勿转发", 600, 1760);
|
||||
context.fillText("本邀请码为一次性使用", 600, 1760);
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user