签入版本

This commit is contained in:
2026-10-10 19:17:02 +08:00
parent 3423dea03f
commit 32f941b631
232 changed files with 4264 additions and 6251 deletions
@@ -289,10 +289,10 @@ class CaptchaService
```vue
<template>
<el-form-item>
<el-input v-model="form.captchaCode" placeholder="验证码" />
<n-form-item>
<n-input v-model:value="form.captchaCode" placeholder="验证码" />
<img :src="captchaImage" @click="refreshCaptcha" style="cursor: pointer;" />
</el-form-item>
</n-form-item>
</template>
<script setup>
@@ -359,38 +359,39 @@ 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>
<n-form :model="form" :rules="rules" ref="formRef">
<n-form-item label="用户名" path="username">
<n-input v-model:value="form.username" placeholder="请输入用户名" />
</n-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password" placeholder="请输入密码" />
</el-form-item>
<n-form-item label="密码" path="password">
<n-input v-model:value="form.password" type="password" placeholder="请输入密码" />
</n-form-item>
<el-form-item label="验证码" prop="code">
<n-form-item label="验证码" path="code">
<div style="display: flex; gap: 10px;">
<el-input v-model="form.code" placeholder="请输入验证码"
style="flex: 1;" @keyup.enter="handleLogin" />
<n-input v-model:value="form.code" placeholder="请输入验证码"
style="flex: 1;" @keyup.enter="handleLogin" />
<img :src="captchaImage" @click="refreshCaptcha"
style="cursor: pointer; height: 40px;" title="点击刷新" />
</div>
</el-form-item>
</n-form-item>
<el-form-item>
<el-button type="primary" @click="handleLogin" :loading="loading">
<n-form-item>
<n-button type="primary" @click="handleLogin" :loading="loading">
登录
</el-button>
</el-form-item>
</el-form>
</n-button>
</n-form-item>
</n-form>
</template>
<script setup lang="ts">
import { getCaptcha } from '@/api/common/user';
import { useUserStore } from '@/store/modules/user';
import { ElMessage } from 'element-plus';
import { useMessage } from 'naive-ui';
const userStore = useUserStore();
const message = useMessage();
const router = useRouter();
const formRef = ref();
@@ -424,11 +425,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 {
@@ -217,12 +217,16 @@ class DictController extends BaseController
```typescript
// HTTP 响应拦截器
import { useMessage } from '@/hooks/web/useMessage';
const $message = useMessage();
axios.interceptors.response.use(response => {
const { code, msg } = response.data;
// 演示模式拦截
if (code === 403 && msg === '演示环境,禁止操作') {
ElMessage.warning('当前为演示环境,不允许执行此操作');
$message.warning('当前为演示环境,不允许执行此操作');
return Promise.reject(response.data);
}
@@ -235,13 +239,13 @@ axios.interceptors.response.use(response => {
```vue
<template>
<!-- 演示模式下禁用删除按钮 -->
<el-button
type="danger"
<n-button
type="error"
:disabled="isDemoMode"
@click="handleDelete"
>
删除
</el-button>
</n-button>
</template>
<script setup>
@@ -206,14 +206,11 @@ export function getDictItemList(code: string) {
```vue
<template>
<el-select v-model="formData.status" placeholder="请选择状态">
<el-option
v-for="item in statusOptions"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
<n-select
v-model:value="formData.status"
:options="statusOptions"
placeholder="请选择状态"
/>
</template>
<script setup>
@@ -272,9 +269,9 @@ const { options: statusOptions } = useDict('user_status');
```vue
<template>
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
<n-tag :type="row.status === 1 ? 'success' : 'error'" bordered>
{{ row.statusText }}
</el-tag>
</n-tag>
</template>
```
@@ -256,14 +256,11 @@ onMounted(async () => {
```vue
<template>
<el-select v-model="form.gender" placeholder="请选择性别">
<el-option
v-for="item in genderOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<n-select
v-model:value="form.gender"
:options="genderOptions"
placeholder="请选择性别"
/>
</template>
```
@@ -8,27 +8,30 @@
### 按钮 loading
ElementPlus 的按钮组件支持 `loading` 属性,请求期间禁用按钮:
NaiveUI 的按钮组件支持 `loading` 属性,请求期间禁用按钮:
```vue
<template>
<el-button
<n-button
type="primary"
:loading="submitting"
@click="handleSubmit"
>
提交
</el-button>
</n-button>
</template>
<script setup>
import { useMessage } from 'naive-ui';
const message = useMessage();
const submitting = ref(false);
async function handleSubmit() {
submitting.value = true;
try {
await exampleAdd(formData.value);
ElMessage.success('添加成功');
message.success('添加成功');
} finally {
submitting.value = false;
}
@@ -41,12 +44,15 @@ async function handleSubmit() {
前端提供 `useLockFn` 钩子函数,实现请求级别的防重复提交:
```typescript
import { useLockFn } from '@/utils/lock';
import { useLockFn } from '@/utils/useLockFn';
import { useMessage } from 'naive-ui';
const message = useMessage();
// 包装提交函数,防止重复点击
const handleSubmit = useLockFn(async () => {
const { lockFn: handleSubmit } = useLockFn(async () => {
await exampleAdd(formData.value);
ElMessage.success('添加成功');
message.success('添加成功');
});
```
@@ -353,10 +353,10 @@ class ArticleLogic extends BaseTenantLogic
```vue
<template>
<!-- 单权限:拥有 sys:user:add 才显示 -->
<el-button v-perm="'sys:user:add'" type="primary">新增</el-button>
<n-button v-perm="'sys:user:add'" type="primary">新增</n-button>
<!-- 多权限:拥有任一权限即显示 -->
<el-button v-perms="['sys:user:add', 'sys:user:update']">操作</el-button>
<n-button v-perms="['sys:user:add', 'sys:user:update']">操作</n-button>
<!-- 无权限的按钮直接从 DOM 移除,而非仅隐藏 -->
</template>