版本更新
Some checks are pending
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Test (windows-latest) (push) Waiting to run
CI / Lint (ubuntu-latest) (push) Waiting to run
CI / Lint (windows-latest) (push) Waiting to run
CI / Check (ubuntu-latest) (push) Waiting to run
CI / Check (windows-latest) (push) Waiting to run
CI / CI OK (push) Blocked by required conditions
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
Deploy Website on push / Deploy Push Playground Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Docs Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Antd Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Element Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Naive Ftp (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run
Some checks are pending
CI / Test (ubuntu-latest) (push) Waiting to run
CI / Test (windows-latest) (push) Waiting to run
CI / Lint (ubuntu-latest) (push) Waiting to run
CI / Lint (windows-latest) (push) Waiting to run
CI / Check (ubuntu-latest) (push) Waiting to run
CI / Check (windows-latest) (push) Waiting to run
CI / CI OK (push) Blocked by required conditions
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
Deploy Website on push / Deploy Push Playground Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Docs Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Antd Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Element Ftp (push) Waiting to run
Deploy Website on push / Deploy Push Naive Ftp (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run
This commit is contained in:
parent
e52eb2ca64
commit
e1d1c87b6b
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -9,7 +9,7 @@
|
|||||||
"url": "http://localhost:5555",
|
"url": "http://localhost:5555",
|
||||||
"env": { "NODE_ENV": "development" },
|
"env": { "NODE_ENV": "development" },
|
||||||
"sourceMaps": true,
|
"sourceMaps": true,
|
||||||
"webRoot": "${workspaceFolder}"
|
"webRoot": "${workspaceFolder}/apps/web-antd"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "chrome",
|
"type": "chrome",
|
||||||
|
|||||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts",
|
"tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts",
|
||||||
// workbench
|
// workbench
|
||||||
|
"editor.fontFamily": "JetBrains Mono, Menlo, Monaco, 'Courier New', monospace",
|
||||||
"workbench.list.smoothScrolling": true,
|
"workbench.list.smoothScrolling": true,
|
||||||
"workbench.startupEditor": "newUntitledFile",
|
"workbench.startupEditor": "newUntitledFile",
|
||||||
"workbench.tree.indent": 10,
|
"workbench.tree.indent": 10,
|
||||||
|
|||||||
28
apps/web-antd/.vscode/launch.json
vendored
28
apps/web-antd/.vscode/launch.json
vendored
@ -6,29 +6,13 @@
|
|||||||
"configurations": [
|
"configurations": [
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Launch via NPM",
|
"type": "chrome",
|
||||||
|
"name": "vben admin antd dev",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"runtimeArgs": [
|
"url": "http://localhost:5666",
|
||||||
"dev",
|
"env": { "NODE_ENV": "development" },
|
||||||
"antd",
|
"sourceMaps": true,
|
||||||
],
|
"webRoot": "${workspaceFolder}"
|
||||||
"runtimeExecutable": "pnpm",
|
|
||||||
"skipFiles": [
|
|
||||||
"<node_internals>/**"
|
|
||||||
],
|
|
||||||
"type": "node"
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"address": "localhost",
|
|
||||||
"localRoot": "${workspaceFolder}",
|
|
||||||
"name": "Attach to Remote",
|
|
||||||
"port": 5666,
|
|
||||||
"remoteRoot": "F:/Github/admin/apps",
|
|
||||||
"request": "attach",
|
|
||||||
"skipFiles": [
|
|
||||||
"<node_internals>/**"
|
|
||||||
],
|
|
||||||
"type": "node"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
3
apps/web-antd/.vscode/settings.json
vendored
Normal file
3
apps/web-antd/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"workbench.colorTheme": "GitHub Light Colorblind (Beta)"
|
||||||
|
}
|
||||||
BIN
apps/web-antd/public/MMM_loading_logo1.png
Normal file
BIN
apps/web-antd/public/MMM_loading_logo1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
17
apps/web-antd/src/api/core/admin.user.ts
Normal file
17
apps/web-antd/src/api/core/admin.user.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
import type { UserInfo } from '#/model/admin.user';
|
||||||
|
|
||||||
|
export interface UserParam {
|
||||||
|
uid: string;
|
||||||
|
}
|
||||||
|
export async function getAdminInfoApi(param: UserParam) {
|
||||||
|
return requestClient.post<UserInfo>('/admin/info', param);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAdminListApi() {
|
||||||
|
return requestClient.post<UserInfo[]>('/admin/list');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addAdminApi(param: UserInfo) {
|
||||||
|
return requestClient.post<UserInfo>('/admin/add', param);
|
||||||
|
}
|
||||||
@ -7,6 +7,11 @@ export namespace AuthApi {
|
|||||||
username?: string;
|
username?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PhoneCodeParams {
|
||||||
|
phoneNumber?: string;
|
||||||
|
code?:string;
|
||||||
|
}
|
||||||
|
|
||||||
/** 登录接口返回值 */
|
/** 登录接口返回值 */
|
||||||
export interface LoginResult {
|
export interface LoginResult {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
@ -25,6 +30,9 @@ export async function loginApi(data: AuthApi.LoginParams) {
|
|||||||
return requestClient.post<AuthApi.LoginResult>('/auth/login', data);
|
return requestClient.post<AuthApi.LoginResult>('/auth/login', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function phoneLoginApi(data: AuthApi.PhoneCodeParams) {
|
||||||
|
return requestClient.post<AuthApi.LoginResult>('/auth/phoneLogin', data);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 刷新accessToken
|
* 刷新accessToken
|
||||||
*/
|
*/
|
||||||
@ -47,5 +55,17 @@ export async function logoutApi() {
|
|||||||
* 获取用户权限码
|
* 获取用户权限码
|
||||||
*/
|
*/
|
||||||
export async function getAccessCodesApi() {
|
export async function getAccessCodesApi() {
|
||||||
return requestClient.get<string[]>('/auth/codes');
|
return requestClient.get<string[]>('/auth/Codes');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取手机验证码
|
||||||
|
* @param phoneNumber
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function getPhoneCodeApi(phoneNumber: string) {
|
||||||
|
return requestClient.post<string>('/auth/phoneCode', {
|
||||||
|
phoneNumber,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
import { requestClient } from '#/api/request';
|
|
||||||
import type { PlayerInfo } from '#/model/player';
|
|
||||||
|
|
||||||
export interface PlayerParam {
|
|
||||||
uid :string
|
|
||||||
}
|
|
||||||
export async function getPlayerInfoApi(param : PlayerParam) {
|
|
||||||
return requestClient.post<PlayerInfo>('/player/info', param);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,13 +1,10 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
|
||||||
|
|
||||||
import { AuthPageLayout } from '@vben/layouts';
|
import { AuthPageLayout } from '@vben/layouts';
|
||||||
import { preferences } from '@vben/preferences';
|
|
||||||
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
const appName = computed(() => preferences.app.name);
|
// const appName = computed(() => preferences.app.name);
|
||||||
const logo = computed(() => preferences.logo.source);
|
const logo = '../../public/MMM_loading_logo1.png';
|
||||||
|
const appName = '喵喵喵之家';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@ -6,6 +6,11 @@
|
|||||||
"qrcodeLogin": "二维码登录",
|
"qrcodeLogin": "二维码登录",
|
||||||
"forgetPassword": "忘记密码"
|
"forgetPassword": "忘记密码"
|
||||||
},
|
},
|
||||||
|
"admin": {
|
||||||
|
"title": "管理中心",
|
||||||
|
"user": "用户管理",
|
||||||
|
"setting": "系统设置"
|
||||||
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"title": "服务器管理",
|
"title": "服务器管理",
|
||||||
"analytics": "分析台",
|
"analytics": "分析台",
|
||||||
|
|||||||
10
apps/web-antd/src/model/admin.user.ts
Normal file
10
apps/web-antd/src/model/admin.user.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export interface UserInfo {
|
||||||
|
username: string;
|
||||||
|
password?: string;
|
||||||
|
phone: string;
|
||||||
|
email?: string;
|
||||||
|
avatar?: string;
|
||||||
|
uid?: string;
|
||||||
|
group: string;
|
||||||
|
role: number;
|
||||||
|
}
|
||||||
@ -1 +1,3 @@
|
|||||||
export * from './player';
|
export * from './admin.user';
|
||||||
|
export * from './app';
|
||||||
|
export * from './node';
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
export interface PlayerInfo {
|
|
||||||
NickName: string;
|
|
||||||
Name:string;
|
|
||||||
AreaId:number;
|
|
||||||
Process:number;
|
|
||||||
Charge:number;
|
|
||||||
ChargeNum :number;
|
|
||||||
}
|
|
||||||
|
|
||||||
32
apps/web-antd/src/router/routes/modules/admin.ts
Normal file
32
apps/web-antd/src/router/routes/modules/admin.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import type { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
import { BasicLayout } from '#/layouts';
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
component: BasicLayout,
|
||||||
|
meta: {
|
||||||
|
icon: 'lucide:layout-dashboard',
|
||||||
|
order: -1,
|
||||||
|
title: $t('page.admin.title'),
|
||||||
|
authority:['super'],
|
||||||
|
},
|
||||||
|
name: 'Admin',
|
||||||
|
path: '/',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'UserManagement',
|
||||||
|
path: '/user-management',
|
||||||
|
component: () => import('#/views/admin/user/index.vue'),
|
||||||
|
meta: {
|
||||||
|
affixTab: true,
|
||||||
|
icon: 'majesticons:user-box-line',
|
||||||
|
title: $t('page.admin.user'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routes;
|
||||||
@ -10,6 +10,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
icon: 'lucide:layout-dashboard',
|
icon: 'lucide:layout-dashboard',
|
||||||
order: -1,
|
order: -1,
|
||||||
title: $t('page.dashboard.title'),
|
title: $t('page.dashboard.title'),
|
||||||
|
authority: ['super'],
|
||||||
},
|
},
|
||||||
name: 'Dashboard',
|
name: 'Dashboard',
|
||||||
path: '/',
|
path: '/',
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
|
|||||||
import { notification } from 'ant-design-vue';
|
import { notification } from 'ant-design-vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
|
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi, phoneLoginApi } from '#/api';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', () => {
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
@ -74,6 +74,56 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function phoneLogin(
|
||||||
|
params: Recordable<any>,
|
||||||
|
onSuccess?: () => Promise<void> | void,
|
||||||
|
) {
|
||||||
|
// 异步处理手机验证码登录操作并获取 accessToken
|
||||||
|
let userInfo: null | UserInfo = null;
|
||||||
|
try {
|
||||||
|
loginLoading.value = true;
|
||||||
|
const { accessToken } = await phoneLoginApi(params);
|
||||||
|
|
||||||
|
// 如果成功获取到 accessToken
|
||||||
|
if (accessToken) {
|
||||||
|
accessStore.setAccessToken(accessToken);
|
||||||
|
|
||||||
|
// 获取用户信息并存储到 accessStore 中
|
||||||
|
const [fetchUserInfoResult, accessCodes] = await Promise.all([
|
||||||
|
fetchUserInfo(),
|
||||||
|
getAccessCodesApi(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
userInfo = fetchUserInfoResult;
|
||||||
|
|
||||||
|
userStore.setUserInfo(userInfo);
|
||||||
|
accessStore.setAccessCodes(accessCodes);
|
||||||
|
|
||||||
|
if (accessStore.loginExpired) {
|
||||||
|
accessStore.setLoginExpired(false);
|
||||||
|
} else {
|
||||||
|
onSuccess
|
||||||
|
? await onSuccess?.()
|
||||||
|
: await router.push(userInfo.homePath || DEFAULT_HOME_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo?.realName) {
|
||||||
|
notification.success({
|
||||||
|
description: `${$t('authentication.loginSuccessDesc')}:${userInfo?.realName}`,
|
||||||
|
duration: 3,
|
||||||
|
message: $t('authentication.loginSuccess'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loginLoading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userInfo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function logout(redirect: boolean = true) {
|
async function logout(redirect: boolean = true) {
|
||||||
try {
|
try {
|
||||||
await logoutApi();
|
await logoutApi();
|
||||||
@ -111,5 +161,6 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
fetchUserInfo,
|
fetchUserInfo,
|
||||||
loginLoading,
|
loginLoading,
|
||||||
logout,
|
logout,
|
||||||
|
phoneLogin,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,21 +1,26 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { VbenFormSchema } from '@vben/common-ui';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
import type { Recordable } from '@vben/types';
|
import type { Recordable } from '@vben/types';
|
||||||
|
import { getPhoneCodeApi, phoneLoginApi } from '#/api';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import { AuthenticationCodeLogin, z } from '@vben/common-ui';
|
import { AuthenticationCodeLogin, z } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
import { useAuthStore } from '#/store';
|
||||||
|
|
||||||
defineOptions({ name: 'CodeLogin' });
|
defineOptions({ name: 'CodeLogin' });
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
const phoneNumber = ref('');
|
||||||
const formSchema = computed((): VbenFormSchema[] => {
|
const formSchema = computed((): VbenFormSchema[] => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
component: 'VbenInput',
|
component: 'VbenInput',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
onChange: (value: any) => {
|
||||||
|
console.log('Phone number changed:', value.target.value);
|
||||||
|
phoneNumber.value = value.target.value;
|
||||||
|
},
|
||||||
placeholder: $t('authentication.mobile'),
|
placeholder: $t('authentication.mobile'),
|
||||||
},
|
},
|
||||||
fieldName: 'phoneNumber',
|
fieldName: 'phoneNumber',
|
||||||
@ -30,6 +35,10 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
{
|
{
|
||||||
component: 'VbenPinInput',
|
component: 'VbenPinInput',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
handleSendCode: async () => {
|
||||||
|
console.log('Sending code to:', phoneNumber.value);
|
||||||
|
getPhoneCodeApi(phoneNumber.value);
|
||||||
|
},
|
||||||
createText: (countdown: number) => {
|
createText: (countdown: number) => {
|
||||||
const text =
|
const text =
|
||||||
countdown > 0
|
countdown > 0
|
||||||
@ -50,16 +59,15 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
* Asynchronously handle the login process
|
* Asynchronously handle the login process
|
||||||
* @param values 登录表单数据
|
* @param values 登录表单数据
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
async function handleLogin(values: Recordable<any>) {
|
async function handleLogin(values: Recordable<any>) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
useAuthStore().phoneLogin(values)
|
||||||
console.log(values);
|
console.log(values);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationCodeLogin
|
<AuthenticationCodeLogin :form-schema="formSchema" :loading="loading" @submit="handleLogin" />
|
||||||
:form-schema="formSchema"
|
|
||||||
:loading="loading"
|
|
||||||
@submit="handleLogin"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -30,20 +30,20 @@ const MOCK_USER_OPTIONS: BasicOption[] = [
|
|||||||
|
|
||||||
const formSchema = computed((): VbenFormSchema[] => {
|
const formSchema = computed((): VbenFormSchema[] => {
|
||||||
return [
|
return [
|
||||||
{
|
// {
|
||||||
component: 'VbenSelect',
|
// component: 'VbenSelect',
|
||||||
componentProps: {
|
// componentProps: {
|
||||||
options: MOCK_USER_OPTIONS,
|
// options: MOCK_USER_OPTIONS,
|
||||||
placeholder: $t('authentication.selectAccount'),
|
// placeholder: $t('authentication.selectAccount'),
|
||||||
},
|
// },
|
||||||
fieldName: 'selectAccount',
|
// fieldName: 'selectAccount',
|
||||||
label: $t('authentication.selectAccount'),
|
// label: $t('authentication.selectAccount'),
|
||||||
rules: z
|
// rules: z
|
||||||
.string()
|
// .string()
|
||||||
.min(1, { message: $t('authentication.selectAccount') })
|
// .min(1, { message: $t('authentication.selectAccount') })
|
||||||
.optional()
|
// .optional()
|
||||||
.default('vben'),
|
// .default('vben'),
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
component: 'VbenInput',
|
component: 'VbenInput',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
@ -94,5 +94,6 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
:form-schema="formSchema"
|
:form-schema="formSchema"
|
||||||
:loading="authStore.loginLoading"
|
:loading="authStore.loginLoading"
|
||||||
@submit="authStore.authLogin"
|
@submit="authStore.authLogin"
|
||||||
|
@codeLoginPath="authStore.authLogin"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
108
apps/web-antd/src/views/admin/user/addUser.vue
Normal file
108
apps/web-antd/src/views/admin/user/addUser.vue
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { addAdminApi } from '#/api/core/admin.user';
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import type { UserInfo } from '#/model/admin.user';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
const [Form, FormApi] = useVbenForm({
|
||||||
|
// 所有表单项共用,可单独在表单内覆盖s
|
||||||
|
commonConfig: {
|
||||||
|
// 所有表单项
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 使用 tailwindcss grid布局
|
||||||
|
// 提交函数
|
||||||
|
// 垂直布局,label和input在不同行,值为vertical
|
||||||
|
layout: 'horizontal',
|
||||||
|
// 水平布局,label和input在同一行
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '用户名',
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
fieldName: 'username',
|
||||||
|
rules: 'required',
|
||||||
|
label: '用户名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'InputPassword',
|
||||||
|
componentProps: {},
|
||||||
|
rules: 'required',
|
||||||
|
fieldName: 'password',
|
||||||
|
label: '密码',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {},
|
||||||
|
rules: 'required',
|
||||||
|
fieldName: 'phone',
|
||||||
|
label: '手机号',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
defaultValue: 1,
|
||||||
|
componentProps: {
|
||||||
|
options: [
|
||||||
|
{ label: '管理员', value: 1 },
|
||||||
|
{ label: '普通用户', value: 2 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
rules: 'required',
|
||||||
|
fieldName: 'role',
|
||||||
|
label: '角色',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
defaultValue: 'xijing',
|
||||||
|
componentProps: {
|
||||||
|
options: [{ label: '蹊径', value: 'xijing' }],
|
||||||
|
},
|
||||||
|
rules: 'required',
|
||||||
|
fieldName: 'group',
|
||||||
|
label: '用户组',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showDefaultActions: false,
|
||||||
|
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||||
|
wrapperClass: 'grid-cols-2',
|
||||||
|
});
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
confirmText: '提交',
|
||||||
|
onOpenChange: () => {},
|
||||||
|
onConfirm: async () => {
|
||||||
|
// 提交表单
|
||||||
|
const values = await FormApi.getValues();
|
||||||
|
const Parms: UserInfo = {
|
||||||
|
username: values.username,
|
||||||
|
password: values.password,
|
||||||
|
phone: values.phone,
|
||||||
|
role: values.role,
|
||||||
|
group: values.group,
|
||||||
|
};
|
||||||
|
await addAdminApi(Parms);
|
||||||
|
modalApi.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
defineOptions({
|
||||||
|
name: 'AddServerModal',
|
||||||
|
});
|
||||||
|
defineExpose({
|
||||||
|
FormApi,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal title="添加服务器" :width="800">
|
||||||
|
<Form />
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
6
apps/web-antd/src/views/admin/user/index.vue
Normal file
6
apps/web-antd/src/views/admin/user/index.vue
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import AnalyticsVisitsTable from './user-table.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<AnalyticsVisitsTable />
|
||||||
|
</template>
|
||||||
61
apps/web-antd/src/views/admin/user/user-table.vue
Normal file
61
apps/web-antd/src/views/admin/user/user-table.vue
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Page } from '@vben/common-ui';
|
||||||
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
import { Button, Card, Space } from 'ant-design-vue';
|
||||||
|
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||||
|
import type { UserInfo } from '#/model/admin.user';
|
||||||
|
import { getAdminListApi } from '#/api/core/admin.user';
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import addUserModal from './addUser.vue';
|
||||||
|
|
||||||
|
const gridOptions: VxeGridProps<UserInfo> = {
|
||||||
|
columns: [
|
||||||
|
{ field: 'username', title: '用户名' },
|
||||||
|
{ field: 'phone', title: '手机号' },
|
||||||
|
{ field: 'role', title: '角色' },
|
||||||
|
{ field: 'group', title: '用户组' },
|
||||||
|
],
|
||||||
|
height: 'auto',
|
||||||
|
pagerConfig: {},
|
||||||
|
proxyConfig: {
|
||||||
|
response: {
|
||||||
|
total: 'total',
|
||||||
|
result: 'data',
|
||||||
|
},
|
||||||
|
ajax: {
|
||||||
|
query: async () => {
|
||||||
|
return await getAdminListApi();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
rowConfig: {
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const [Grid, GridApi] = useVbenVxeGrid({ gridOptions });
|
||||||
|
const [addUserM, addUserApi] = useVbenModal({
|
||||||
|
connectedComponent: addUserModal,
|
||||||
|
onClosed: async () => {
|
||||||
|
addUserApi.close();
|
||||||
|
GridApi.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const addAdmin = () => {
|
||||||
|
addUserApi.open();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height>
|
||||||
|
<addUserM class="w-[50%]" />
|
||||||
|
<Card class="mb-5" title="用户操作">
|
||||||
|
<Space>
|
||||||
|
<Button @click="addAdmin">新增</Button>
|
||||||
|
<Button> 删除 </Button>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
<Grid />
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
@ -1,5 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
||||||
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { addMailApi } from '#/api/core/mail';
|
import { addMailApi } from '#/api/core/mail';
|
||||||
@ -11,7 +10,6 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
// 所有表单项
|
// 所有表单项
|
||||||
componentProps: {
|
componentProps: {
|
||||||
class: 'w-full h-full',
|
class: 'w-full h-full',
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 使用 tailwindcss grid布局
|
// 使用 tailwindcss grid布局
|
||||||
@ -25,7 +23,7 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'Title',
|
fieldName: 'Title',
|
||||||
label: '邮件标题',
|
label: '邮件标题',
|
||||||
rules: "required",
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
@ -40,13 +38,13 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
rows: 8,
|
rows: 8,
|
||||||
},
|
},
|
||||||
rules: "required",
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'TitleEN',
|
fieldName: 'TitleEN',
|
||||||
label: '英文邮件标题',
|
label: '英文邮件标题',
|
||||||
rules: "required",
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
@ -61,7 +59,7 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
rows: 8,
|
rows: 8,
|
||||||
},
|
},
|
||||||
rules: "required",
|
rules: 'required',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Textarea',
|
component: 'Textarea',
|
||||||
@ -92,14 +90,14 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: '节日邮件',
|
label: '节日邮件',
|
||||||
value: 2
|
value: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '普通邮件',
|
label: '普通邮件',
|
||||||
value:1
|
value: 1,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
@ -110,14 +108,14 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: '个人邮件',
|
label: '个人邮件',
|
||||||
value: 2
|
value: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '全服邮件',
|
label: '全服邮件',
|
||||||
value:1
|
value: 1,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Textarea',
|
component: 'Textarea',
|
||||||
@ -130,31 +128,40 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
rows: 4,
|
rows: 4,
|
||||||
},
|
},
|
||||||
dependencies: {
|
dependencies: {
|
||||||
|
triggerFields: ['send_type'],
|
||||||
componentProps(values) {
|
componentProps(values) {
|
||||||
if(values.mail_type === 2) {
|
console.log('value' + values.send_type);
|
||||||
|
if (values.send_type === 2) {
|
||||||
return {
|
return {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rules: "required",
|
rules: 'required',
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
triggerFields: ['mail_type'],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
});
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
confirmText: '提交',
|
confirmText: '提交',
|
||||||
|
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const values = await FormApi.getValues();
|
const values = await FormApi.getValues();
|
||||||
const start_time = values.start_time && values.start_time.length > 0 ? dayjs(values.start_time[0]).unix() : 0;
|
const start_time =
|
||||||
const end_time = values.start_time && values.start_time.length > 0 ? dayjs(values.start_time[1]).unix() : 0;
|
values.start_time && values.start_time.length > 0
|
||||||
const register_time = values.register_time ? dayjs(values.register_time).unix() : 0;
|
? dayjs(values.start_time[0]).unix()
|
||||||
|
: 0;
|
||||||
|
const end_time =
|
||||||
|
values.start_time && values.start_time.length > 0
|
||||||
|
? dayjs(values.start_time[1]).unix()
|
||||||
|
: 0;
|
||||||
|
const register_time = values.register_time
|
||||||
|
? dayjs(values.register_time).unix()
|
||||||
|
: 0;
|
||||||
const mail_type = values.mail_type;
|
const mail_type = values.mail_type;
|
||||||
const send_type = values.send_type;
|
const send_type = values.send_type;
|
||||||
const Title = values.Title;
|
const Title = values.Title;
|
||||||
@ -182,16 +189,15 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
register_time: register_time,
|
register_time: register_time,
|
||||||
mail_type: mail_type,
|
mail_type: mail_type,
|
||||||
send_type: send_type,
|
send_type: send_type,
|
||||||
}
|
};
|
||||||
await addMailApi(param);
|
await addMailApi(param);
|
||||||
modalApi.close();
|
modalApi.close();
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'AddMailModal',
|
name: 'AddMailModal',
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Modal :width="800" title="添加邮件">
|
<Modal :width="800" title="添加邮件">
|
||||||
|
|||||||
@ -24,8 +24,13 @@ const formOptions: VbenFormProps = {
|
|||||||
defaultValue: 1,
|
defaultValue: 1,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
onChange: async (value: number) => {
|
onChange: async (value: number) => {
|
||||||
const serverResponse = await getServerListApi({ AppId: value, Type: 1 });
|
const serverResponse = await getServerListApi({
|
||||||
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
|
AppId: value,
|
||||||
|
Type: 1,
|
||||||
|
});
|
||||||
|
ServerList.value = Array.isArray(serverResponse)
|
||||||
|
? serverResponse
|
||||||
|
: [];
|
||||||
GridApi.formApi.updateSchema([
|
GridApi.formApi.updateSchema([
|
||||||
{
|
{
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
@ -40,9 +45,7 @@ const formOptions: VbenFormProps = {
|
|||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
filterOption: true,
|
filterOption: true,
|
||||||
options: [
|
options: [],
|
||||||
|
|
||||||
],
|
|
||||||
placeholder: '请选择',
|
placeholder: '请选择',
|
||||||
showSearch: true,
|
showSearch: true,
|
||||||
},
|
},
|
||||||
@ -68,7 +71,7 @@ const formOptions: VbenFormProps = {
|
|||||||
submitOnChange: false,
|
submitOnChange: false,
|
||||||
// 按下回车时是否提交表单
|
// 按下回车时是否提交表单
|
||||||
submitOnEnter: false,
|
submitOnEnter: false,
|
||||||
}
|
};
|
||||||
const gridOptions: VxeGridProps<MailData> = {
|
const gridOptions: VxeGridProps<MailData> = {
|
||||||
columns: [
|
columns: [
|
||||||
{ field: 'mail_id', title: 'id' },
|
{ field: 'mail_id', title: 'id' },
|
||||||
@ -79,8 +82,16 @@ const gridOptions: VxeGridProps<MailData> = {
|
|||||||
{ field: 'content', title: '邮件内容' },
|
{ field: 'content', title: '邮件内容' },
|
||||||
{ field: 'content_en', title: '英文邮件内容' },
|
{ field: 'content_en', title: '英文邮件内容' },
|
||||||
{ field: 'items', title: '道具' },
|
{ field: 'items', title: '道具' },
|
||||||
{ field: 'mail_type', title: '邮件类型' , formatter: ({ cellValue }) => cellValue == 1 ? '普通邮件' : '节日邮件'},
|
{
|
||||||
{ field: 'send_type', title: '邮件发送类型' , formatter: ({ cellValue }) => cellValue == 1 ? '全服邮件' : '个人邮件'},
|
field: 'mail_type',
|
||||||
|
title: '邮件类型',
|
||||||
|
formatter: ({ cellValue }) => (cellValue == 1 ? '普通邮件' : '节日邮件'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'send_type',
|
||||||
|
title: '邮件发送类型',
|
||||||
|
formatter: ({ cellValue }) => (cellValue == 1 ? '全服邮件' : '个人邮件'),
|
||||||
|
},
|
||||||
{ field: 'to_uids', title: '接收者' },
|
{ field: 'to_uids', title: '接收者' },
|
||||||
{ field: 'create_time', title: '创建时间' },
|
{ field: 'create_time', title: '创建时间' },
|
||||||
{ slots: { default: 'action' }, title: '操作', width: 100 },
|
{ slots: { default: 'action' }, title: '操作', width: 100 },
|
||||||
@ -89,8 +100,8 @@ const gridOptions: VxeGridProps<MailData> = {
|
|||||||
pagerConfig: {},
|
pagerConfig: {},
|
||||||
proxyConfig: {
|
proxyConfig: {
|
||||||
response: {
|
response: {
|
||||||
total: "total",
|
total: 'total',
|
||||||
result: "data"
|
result: 'data',
|
||||||
},
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
query: async ({ page }, formValues) => {
|
query: async ({ page }, formValues) => {
|
||||||
@ -116,12 +127,16 @@ const gridEvents: VxeGridListeners<MailData> = {
|
|||||||
// message.info(`cell-click: ${row.title}`);
|
// message.info(`cell-click: ${row.title}`);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const [Grid, GridApi] = useVbenVxeGrid({ gridEvents, formOptions, gridOptions });
|
const [Grid, GridApi] = useVbenVxeGrid({
|
||||||
|
gridEvents,
|
||||||
|
formOptions,
|
||||||
|
gridOptions,
|
||||||
|
});
|
||||||
const [AddMailM, AddMailApi] = useVbenModal({
|
const [AddMailM, AddMailApi] = useVbenModal({
|
||||||
connectedComponent: AddMailModal,
|
connectedComponent: AddMailModal,
|
||||||
onClosed: async () => {
|
onClosed: async () => {
|
||||||
AddMailApi.close();
|
AddMailApi.close();
|
||||||
GridApi.query()
|
GridApi.query();
|
||||||
//console.log("close")
|
//console.log("close")
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -147,7 +162,10 @@ onMounted(async () => {
|
|||||||
fieldName: 'AppId',
|
fieldName: 'AppId',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const serverResponse = await getServerListApi({ AppId: app.AppId, Type: 1 });
|
const serverResponse = await getServerListApi({
|
||||||
|
AppId: app.AppId,
|
||||||
|
Type: 1,
|
||||||
|
});
|
||||||
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
|
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
|
||||||
GridApi.formApi.updateSchema([
|
GridApi.formApi.updateSchema([
|
||||||
{
|
{
|
||||||
@ -185,8 +203,6 @@ async function deleteRow(row: MailData) {
|
|||||||
GridApi.setLoading(false);
|
GridApi.setLoading(false);
|
||||||
GridApi.query();
|
GridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -200,7 +216,9 @@ async function deleteRow(row: MailData) {
|
|||||||
</Card>
|
</Card>
|
||||||
<Grid>
|
<Grid>
|
||||||
<template #action="{ row }">
|
<template #action="{ row }">
|
||||||
<Button type="link" @click=deleteRow(row) style="color:#cc0000">删除</Button>
|
<Button type="link" @click="deleteRow(row)" style="color: #cc0000"
|
||||||
|
>删除</Button
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@ -9,5 +9,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"references": [{ "path": "./tsconfig.node.json" }],
|
"references": [{ "path": "./tsconfig.node.json" }],
|
||||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
|
"sourceMap": true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
submit: [Recordable<any>];
|
submit: [Recordable<any>];
|
||||||
|
sendCode: [Recordable<any>];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -99,14 +100,9 @@ defineExpose({
|
|||||||
</template>
|
</template>
|
||||||
</Title>
|
</Title>
|
||||||
<Form />
|
<Form />
|
||||||
<VbenButton
|
<VbenButton :class="{
|
||||||
:class="{
|
|
||||||
'cursor-wait': loading,
|
'cursor-wait': loading,
|
||||||
}"
|
}" :loading="loading" class="w-full" @click="handleSubmit">
|
||||||
:loading="loading"
|
|
||||||
class="w-full"
|
|
||||||
@click="handleSubmit"
|
|
||||||
>
|
|
||||||
<slot name="submitButtonText">
|
<slot name="submitButtonText">
|
||||||
{{ submitButtonText || $t('common.login') }}
|
{{ submitButtonText || $t('common.login') }}
|
||||||
</slot>
|
</slot>
|
||||||
|
|||||||
@ -165,11 +165,11 @@ defineExpose({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 第三方登录 -->
|
<!-- 第三方登录 -->
|
||||||
<slot name="third-party-login">
|
<!-- <slot name="third-party-login">
|
||||||
<ThirdPartyLogin v-if="showThirdPartyLogin" />
|
<ThirdPartyLogin v-if="showThirdPartyLogin" />
|
||||||
</slot>
|
</slot> -->
|
||||||
|
|
||||||
<slot name="to-register">
|
<!-- <slot name="to-register">
|
||||||
<div v-if="showRegister" class="mt-3 text-center text-sm">
|
<div v-if="showRegister" class="mt-3 text-center text-sm">
|
||||||
{{ $t('authentication.accountTip') }}
|
{{ $t('authentication.accountTip') }}
|
||||||
<span
|
<span
|
||||||
@ -179,6 +179,6 @@ defineExpose({
|
|||||||
{{ $t('authentication.createAccount') }}
|
{{ $t('authentication.createAccount') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -30,6 +30,10 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
{
|
{
|
||||||
component: 'VbenPinInput',
|
component: 'VbenPinInput',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
|
updated() {
|
||||||
|
loading.value = false;
|
||||||
|
console.log('PinInput updated');
|
||||||
|
},
|
||||||
createText: (countdown: number) => {
|
createText: (countdown: number) => {
|
||||||
const text =
|
const text =
|
||||||
countdown > 0
|
countdown > 0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user