feat:增加主包(tabbar)、system(系统管理)、infra(基础设施)、bpm(工作流程)的页面
This commit is contained in:
90
src/pages/auth/components/code-input.vue
Normal file
90
src/pages/auth/components/code-input.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<view class="input-item">
|
||||
<wd-icon name="lock-on" size="20px" color="#1890ff" />
|
||||
<wd-input
|
||||
:model-value="modelValue"
|
||||
placeholder="请输入验证码"
|
||||
clearable
|
||||
clear-trigger="focus"
|
||||
no-border
|
||||
type="number"
|
||||
:maxlength="6"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
/>
|
||||
<view
|
||||
class="whitespace-nowrap border-l-1rpx border-l-[#e5e5e5] border-l-solid px-20rpx text-28rpx text-[#1890ff]"
|
||||
@click="handleSendCode"
|
||||
>
|
||||
<text :class="{ 'text-gray-400': countdown > 0 }">
|
||||
{{ countdown > 0 ? `${countdown} 秒后重发` : "获取验证码" }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onUnmounted, ref } from "vue";
|
||||
import { useToast } from "wot-design-uni";
|
||||
import { sendSmsCode } from "@/api/login";
|
||||
import { isMobile } from "@/utils/validator";
|
||||
|
||||
defineOptions({
|
||||
name: "CodeInput",
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string; // 验证码值 (v-model)
|
||||
mobile: string; // 手机号
|
||||
scene: number; // 短信场景:21-登录 23-重置密码
|
||||
beforeSend?: () => boolean; // 发送前的校验函数,返回 false 则不发送
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
"update:modelValue": [value: string];
|
||||
}>();
|
||||
|
||||
const toast = useToast();
|
||||
const countdown = ref(0); // 验证码倒计时,单位秒
|
||||
let countdownTimer: ReturnType<typeof setInterval> | null = null; // 倒计时定时器
|
||||
|
||||
/** 页面卸载时清除倒计时定时器 */
|
||||
onUnmounted(() => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
countdownTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
/** 发送验证码 */
|
||||
async function handleSendCode() {
|
||||
// 执行前置校验
|
||||
if (props.beforeSend && !props.beforeSend()) {
|
||||
return;
|
||||
}
|
||||
if (countdown.value > 0) {
|
||||
return;
|
||||
}
|
||||
if (!props.mobile) {
|
||||
toast.warning("请输入手机号");
|
||||
return;
|
||||
}
|
||||
if (!isMobile(props.mobile)) {
|
||||
toast.warning("请输入正确的手机号");
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
await sendSmsCode({ mobile: props.mobile, scene: props.scene });
|
||||
toast.success("验证码已发送");
|
||||
|
||||
// 开始倒计时
|
||||
countdown.value = 60;
|
||||
countdownTimer = setInterval(() => {
|
||||
countdown.value--;
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(countdownTimer!);
|
||||
countdownTimer = null;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
</script>
|
||||
12
src/pages/auth/components/header.vue
Normal file
12
src/pages/auth/components/header.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<view class="header flex flex-col items-center pb-60rpx pt-120rpx">
|
||||
<image class="mb-24rpx h-160rpx w-160rpx" src="/static/logo.svg" mode="aspectFit" />
|
||||
<view class="text-44rpx text-[#1890ff] font-bold">
|
||||
{{ title }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const title = import.meta.env.VITE_APP_TITLE // 应用标题
|
||||
</script>
|
||||
134
src/pages/auth/components/tenant-picker.vue
Normal file
134
src/pages/auth/components/tenant-picker.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<view v-if="tenantEnabled" class="input-item">
|
||||
<wd-icon name="home" size="20px" color="#1890ff" />
|
||||
<wd-picker
|
||||
:model-value="tenantId"
|
||||
:columns="tenantList"
|
||||
label-key="name"
|
||||
value-key="id"
|
||||
label=""
|
||||
placeholder="请选择租户"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useToast } from "wot-design-uni";
|
||||
import {
|
||||
getTenantByWebsite,
|
||||
getTenantSimpleList,
|
||||
type TenantVO,
|
||||
} from "@/api/login";
|
||||
import { useUserStore } from "@/store/user";
|
||||
|
||||
const toast = useToast();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const tenantEnabled = computed(
|
||||
() => import.meta.env.VITE_APP_TENANT_ENABLE === "true",
|
||||
); // 租户开关:通过环境变量控制
|
||||
const tenantList = ref<TenantVO[]>([]); // 租户列表数据
|
||||
|
||||
const tenantId = computed(
|
||||
() =>
|
||||
userStore.tenantId ||
|
||||
Number(import.meta.env.VITE_APP_DEFAULT_LOGIN_TENANT_ID) ||
|
||||
undefined,
|
||||
); // 当前选中的租户
|
||||
|
||||
/** 获取租户列表,并根据域名/appId 自动选中租户 */
|
||||
async function fetchTenantList() {
|
||||
if (!tenantEnabled.value) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 1. 并行获取租户列表和域名对应的租户
|
||||
const websiteTenantPromise = fetchTenantByWebsite();
|
||||
const list = await getTenantSimpleList();
|
||||
tenantList.value = list || [];
|
||||
|
||||
// 2. 确定选中的租户:域名/appId > store 中的租户 > 列表第一个
|
||||
let selectedTenantId: number | null = null;
|
||||
// 2.1 优先使用域名/appId 对应的租户
|
||||
const websiteTenant = await websiteTenantPromise;
|
||||
if (websiteTenant?.id) {
|
||||
selectedTenantId = websiteTenant.id;
|
||||
}
|
||||
// 2.2 如果没有从域名获取到,使用 store 中的租户
|
||||
if (!selectedTenantId && userStore.tenantId) {
|
||||
selectedTenantId = userStore.tenantId;
|
||||
}
|
||||
// 2.3 如果还是没有,使用列表第一个
|
||||
if (!selectedTenantId && tenantList.value.length > 0) {
|
||||
selectedTenantId = tenantList.value[0].id;
|
||||
}
|
||||
|
||||
// 3. 设置选中的租户
|
||||
if (selectedTenantId && selectedTenantId !== userStore.tenantId) {
|
||||
userStore.setTenantId(selectedTenantId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取租户列表失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/** 根据域名或 appId 获取租户 */
|
||||
async function fetchTenantByWebsite(): Promise<TenantVO | null> {
|
||||
try {
|
||||
let website: string | null = null;
|
||||
|
||||
// #ifdef H5
|
||||
// H5 环境:使用域名
|
||||
if (window?.location?.hostname) {
|
||||
website = window.location.hostname;
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP
|
||||
// 小程序环境:使用 appId
|
||||
const appId = uni.getAccountInfoSync?.()?.miniProgram?.appId;
|
||||
if (appId) {
|
||||
website = appId;
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (website) {
|
||||
return await getTenantByWebsite(website);
|
||||
}
|
||||
} catch (error) {
|
||||
// 域名未配置租户时会报错,忽略即可
|
||||
console.debug("根据域名获取租户失败:", error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 租户选择确认 */
|
||||
function handleConfirm({ value }: { value: number }) {
|
||||
userStore.setTenantId(value);
|
||||
}
|
||||
|
||||
/** 校验租户是否已选择 */
|
||||
function validate(): boolean {
|
||||
if (!tenantEnabled.value) {
|
||||
return true;
|
||||
}
|
||||
if (!tenantId.value) {
|
||||
toast.warning("请选择租户");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 页面加载时获取租户列表 */
|
||||
onMounted(() => {
|
||||
fetchTenantList();
|
||||
});
|
||||
|
||||
defineExpose({ validate });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../styles/auth.scss";
|
||||
</style>
|
||||
Reference in New Issue
Block a user