签入版本
This commit is contained in:
@@ -289,10 +289,10 @@ class CaptchaService
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-form-item>
|
||||
<el-input v-model="form.captchaCode" placeholder="验证码" />
|
||||
<a-form-item>
|
||||
<a-input v-model:value="form.captchaCode" placeholder="验证码" allow-clear />
|
||||
<img :src="captchaImage" @click="refreshCaptcha" style="cursor: pointer;" />
|
||||
</el-form-item>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -359,36 +359,36 @@ CAPTCHA_RATE_LIMIT = 20
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-form :model="form" :rules="rules" ref="formRef">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model="form.username" placeholder="请输入用户名" />
|
||||
</el-form-item>
|
||||
<a-form :model="form" :rules="rules" ref="formRef">
|
||||
<a-form-item label="用户名" name="username">
|
||||
<a-input v-model:value="form.username" placeholder="请输入用户名" allow-clear />
|
||||
</a-form-item>
|
||||
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input v-model="form.password" type="password" placeholder="请输入密码" />
|
||||
</el-form-item>
|
||||
<a-form-item label="密码" name="password">
|
||||
<a-input-password v-model:value="form.password" placeholder="请输入密码" />
|
||||
</a-form-item>
|
||||
|
||||
<el-form-item label="验证码" prop="code">
|
||||
<a-form-item label="验证码" name="code">
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<el-input v-model="form.code" placeholder="请输入验证码"
|
||||
style="flex: 1;" @keyup.enter="handleLogin" />
|
||||
<a-input v-model:value="form.code" placeholder="请输入验证码"
|
||||
style="flex: 1;" @keyup.enter="handleLogin" allow-clear />
|
||||
<img :src="captchaImage" @click="refreshCaptcha"
|
||||
style="cursor: pointer; height: 40px;" title="点击刷新" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</a-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleLogin" :loading="loading">
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="handleLogin" :loading="loading">
|
||||
登录
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCaptcha } from '@/api/common/user';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
@@ -405,9 +405,9 @@ const form = ref({
|
||||
});
|
||||
|
||||
const rules = {
|
||||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
|
||||
username: { required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
password: { required: true, message: '请输入密码', trigger: 'blur' },
|
||||
code: { required: true, message: '请输入验证码', trigger: 'blur' },
|
||||
};
|
||||
|
||||
// 获取验证码
|
||||
@@ -424,11 +424,11 @@ const handleLogin = async () => {
|
||||
|
||||
try {
|
||||
await userStore.login(form.value);
|
||||
ElMessage.success('登录成功');
|
||||
message.success('登录成功');
|
||||
router.push('/');
|
||||
} catch (e: any) {
|
||||
// 登录失败(包括验证码错误),刷新验证码
|
||||
ElMessage.error(e.message || '登录失败');
|
||||
message.error(e.message || '登录失败');
|
||||
refreshCaptcha();
|
||||
form.value.code = '';
|
||||
} finally {
|
||||
|
||||
@@ -222,7 +222,7 @@ axios.interceptors.response.use(response => {
|
||||
|
||||
// 演示模式拦截
|
||||
if (code === 403 && msg === '演示环境,禁止操作') {
|
||||
ElMessage.warning('当前为演示环境,不允许执行此操作');
|
||||
message.warning('当前为演示环境,不允许执行此操作');
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
|
||||
@@ -235,13 +235,13 @@ axios.interceptors.response.use(response => {
|
||||
```vue
|
||||
<template>
|
||||
<!-- 演示模式下禁用删除按钮 -->
|
||||
<el-button
|
||||
type="danger"
|
||||
<a-button
|
||||
danger
|
||||
:disabled="isDemoMode"
|
||||
@click="handleDelete"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
@@ -206,14 +206,16 @@ export function getDictItemList(code: string) {
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-select v-model="formData.status" placeholder="请选择状态">
|
||||
<el-option
|
||||
<a-select v-model:value="formData.status" placeholder="请选择状态" allow-clear>
|
||||
<a-select-option
|
||||
v-for="item in statusOptions"
|
||||
:key="item.value"
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
:label="item.name"
|
||||
>
|
||||
{{ item.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -240,8 +242,12 @@ const [genderOptions, statusOptions] = await Promise.all([
|
||||
|
||||
### 封装为通用 Hook
|
||||
|
||||
:::warning 说明
|
||||
`ui/src/hooks/web/useDict.ts` 当前项目中尚不存在,以下为建议的封装写法,可按需自行创建。
|
||||
:::
|
||||
|
||||
```typescript
|
||||
// ui/src/hooks/web/useDict.ts
|
||||
// ui/src/hooks/web/useDict.ts(自行创建)
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { getDictItemList } from '@/api/common/index';
|
||||
|
||||
@@ -272,9 +278,9 @@ const { options: statusOptions } = useDict('user_status');
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
<a-tag :color="row.status === 1 ? 'success' : 'error'">
|
||||
{{ row.statusText }}
|
||||
</el-tag>
|
||||
</a-tag>
|
||||
</template>
|
||||
```
|
||||
|
||||
|
||||
@@ -256,14 +256,16 @@ onMounted(async () => {
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-select v-model="form.gender" placeholder="请选择性别">
|
||||
<el-option
|
||||
<a-select v-model:value="form.gender" placeholder="请选择性别" allow-clear>
|
||||
<a-select-option
|
||||
v-for="item in genderOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
:label="item.label"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
```
|
||||
|
||||
|
||||
@@ -8,27 +8,29 @@
|
||||
|
||||
### 按钮 loading
|
||||
|
||||
ElementPlus 的按钮组件支持 `loading` 属性,请求期间禁用按钮:
|
||||
Ant Design 的按钮组件支持 `loading` 属性,请求期间禁用按钮:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-button
|
||||
<a-button
|
||||
type="primary"
|
||||
:loading="submitting"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
提交
|
||||
</el-button>
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
const submitting = ref(false);
|
||||
|
||||
async function handleSubmit() {
|
||||
submitting.value = true;
|
||||
try {
|
||||
await exampleAdd(formData.value);
|
||||
ElMessage.success('添加成功');
|
||||
message.success('添加成功');
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
@@ -41,29 +43,34 @@ async function handleSubmit() {
|
||||
前端提供 `useLockFn` 钩子函数,实现请求级别的防重复提交:
|
||||
|
||||
```typescript
|
||||
import { useLockFn } from '@/utils/lock';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { useLockFn } from '@/utils/useLockFn';
|
||||
|
||||
// 包装提交函数,防止重复点击
|
||||
const handleSubmit = useLockFn(async () => {
|
||||
const { isLock, lockFn: handleSubmit } = useLockFn(async () => {
|
||||
await exampleAdd(formData.value);
|
||||
ElMessage.success('添加成功');
|
||||
message.success('添加成功');
|
||||
});
|
||||
```
|
||||
|
||||
`useLockFn` 原理:
|
||||
|
||||
```typescript
|
||||
function useLockFn(fn: Function) {
|
||||
let isLocked = false;
|
||||
return async (...args: any[]) => {
|
||||
if (isLocked) return;
|
||||
isLocked = true;
|
||||
// ui/src/utils/useLockFn.ts
|
||||
import { ref } from 'vue';
|
||||
|
||||
export function useLockFn(fn: (...args: any[]) => Promise<any>) {
|
||||
const isLock = ref(false);
|
||||
const lockFn = async (...args: any[]) => {
|
||||
if (isLock.value) return;
|
||||
isLock.value = true;
|
||||
try {
|
||||
return await fn(...args);
|
||||
await fn(...args);
|
||||
} finally {
|
||||
isLocked = false;
|
||||
isLock.value = false;
|
||||
}
|
||||
};
|
||||
return { isLock, lockFn };
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -353,10 +353,10 @@ class ArticleLogic extends BaseTenantLogic
|
||||
```vue
|
||||
<template>
|
||||
<!-- 单权限:拥有 sys:user:add 才显示 -->
|
||||
<el-button v-perm="'sys:user:add'" type="primary">新增</el-button>
|
||||
<a-button v-perm="'sys:user:add'" type="primary">新增</a-button>
|
||||
|
||||
<!-- 多权限:拥有任一权限即显示 -->
|
||||
<el-button v-perms="['sys:user:add', 'sys:user:update']">操作</el-button>
|
||||
<a-button v-perms="['sys:user:add', 'sys:user:update']">操作</a-button>
|
||||
|
||||
<!-- 无权限的按钮直接从 DOM 移除,而非仅隐藏 -->
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user