版本更新
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:
hahwu 2025-07-28 16:23:02 +08:00
parent e52eb2ca64
commit e1d1c87b6b
27 changed files with 577 additions and 265 deletions

2
.vscode/launch.json vendored
View File

@ -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",

View File

@ -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,

View File

@ -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
View File

@ -0,0 +1,3 @@
{
"workbench.colorTheme": "GitHub Light Colorblind (Beta)"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View 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);
}

View File

@ -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,
});
}

View File

@ -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);
}

View File

@ -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>

View File

@ -6,6 +6,11 @@
"qrcodeLogin": "二维码登录", "qrcodeLogin": "二维码登录",
"forgetPassword": "忘记密码" "forgetPassword": "忘记密码"
}, },
"admin": {
"title": "管理中心",
"user": "用户管理",
"setting": "系统设置"
},
"dashboard": { "dashboard": {
"title": "服务器管理", "title": "服务器管理",
"analytics": "分析台", "analytics": "分析台",

View File

@ -0,0 +1,10 @@
export interface UserInfo {
username: string;
password?: string;
phone: string;
email?: string;
avatar?: string;
uid?: string;
group: string;
role: number;
}

View File

@ -1 +1,3 @@
export * from './player'; export * from './admin.user';
export * from './app';
export * from './node';

View File

@ -1,9 +0,0 @@
export interface PlayerInfo {
NickName: string;
Name:string;
AreaId:number;
Process:number;
Charge:number;
ChargeNum :number;
}

View 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;

View File

@ -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: '/',

View File

@ -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,
}; };
}); });

View File

@ -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>

View File

@ -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>

View 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
//
// labelinputvertical
layout: 'horizontal',
// labelinput
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,
// 321
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>

View File

@ -0,0 +1,6 @@
<script lang="ts" setup>
import AnalyticsVisitsTable from './user-table.vue';
</script>
<template>
<AnalyticsVisitsTable />
</template>

View 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>

View File

@ -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="添加邮件">

View File

@ -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>

View File

@ -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
} }

View File

@ -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>

View File

@ -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>

View File

@ -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