版本更新
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",
"env": { "NODE_ENV": "development" },
"sourceMaps": true,
"webRoot": "${workspaceFolder}"
"webRoot": "${workspaceFolder}/apps/web-antd"
},
{
"type": "chrome",

View File

@ -1,6 +1,7 @@
{
"tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts",
// workbench
"editor.fontFamily": "JetBrains Mono, Menlo, Monaco, 'Courier New', monospace",
"workbench.list.smoothScrolling": true,
"workbench.startupEditor": "newUntitledFile",
"workbench.tree.indent": 10,

View File

@ -6,29 +6,13 @@
"configurations": [
{
"name": "Launch via NPM",
"request": "launch",
"runtimeArgs": [
"dev",
"antd",
],
"runtimeExecutable": "pnpm",
"skipFiles": [
"<node_internals>/**"
],
"type": "node"
"type": "chrome",
"name": "vben admin antd dev",
"request": "launch",
"url": "http://localhost:5666",
"env": { "NODE_ENV": "development" },
"sourceMaps": true,
"webRoot": "${workspaceFolder}"
},
{
"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;
}
export interface PhoneCodeParams {
phoneNumber?: string;
code?:string;
}
/** 登录接口返回值 */
export interface LoginResult {
accessToken: string;
@ -25,6 +30,9 @@ export async function loginApi(data: AuthApi.LoginParams) {
return requestClient.post<AuthApi.LoginResult>('/auth/login', data);
}
export async function phoneLoginApi(data: AuthApi.PhoneCodeParams) {
return requestClient.post<AuthApi.LoginResult>('/auth/phoneLogin', data);
}
/**
* accessToken
*/
@ -47,5 +55,17 @@ export async function logoutApi() {
*
*/
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>
import { computed } from 'vue';
import { AuthPageLayout } from '@vben/layouts';
import { preferences } from '@vben/preferences';
import { $t } from '#/locales';
const appName = computed(() => preferences.app.name);
const logo = computed(() => preferences.logo.source);
// const appName = computed(() => preferences.app.name);
const logo = '../../public/MMM_loading_logo1.png';
const appName = '喵喵喵之家';
</script>
<template>

View File

@ -6,6 +6,11 @@
"qrcodeLogin": "二维码登录",
"forgetPassword": "忘记密码"
},
"admin": {
"title": "管理中心",
"user": "用户管理",
"setting": "系统设置"
},
"dashboard": {
"title": "服务器管理",
"analytics": "分析台",
@ -14,41 +19,41 @@
"mysql-list": "MySQL列表",
"app-list": "应用列表"
},
"userlog":{
"userlog": {
"title": "玩家管理",
"userlist": "玩家列表",
"assetlog": "资产流水",
"eventlog": "事件日志",
"orderlog": "订单日志"
},
"operation":{
"operation": {
"title": "运营管理",
"level": "等级分布",
"mail": "邮件管理"
},
"log":{
"log": {
"event": {
"order_finish": "完成订单",
"time_limited_event_enable": "开启限时事件",
"collection_add": "添加收藏",
"mutil_merge_change": "合并变更",
"sell_item": "出售物品",
"buy_product_diamond":"产物商店购买",
"store_buy":"商店购买",
"refresh_store_diamond":"刷新商店",
"buy_product_diamond": "产物商店购买",
"store_buy": "商店购买",
"refresh_store_diamond": "刷新商店",
"one_click_decoration": "一键装饰",
"championship_reward": "冠军奖励",
"piggy_bank_open": "猪猪银行开启",
"get_chest": "获得宝箱",
"weekly_gift": "七日签到奖励",
"finish_deco":"装饰",
"finish_deco": "装饰",
"plot_unlock": "解锁场景",
"scene_reward": "场景奖励",
"piggy_bank_income": "猪猪银行收益",
"card_pack_open": "开启卡包",
"get_new_card": "获得新卡牌",
"gift_free":"商店免费奖励",
"item_change":"物品变更",
"gift_free": "商店免费奖励",
"item_change": "物品变更",
"daily_task": "日常任务",
"weekly_task": "每周任务",
"level_up": "升级",

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',
order: -1,
title: $t('page.dashboard.title'),
authority: ['super'],
},
name: 'Dashboard',
path: '/',

View File

@ -9,7 +9,7 @@ import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
import { notification } from 'ant-design-vue';
import { defineStore } from 'pinia';
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi, phoneLoginApi } from '#/api';
import { $t } from '#/locales';
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) {
try {
await logoutApi();
@ -111,5 +161,6 @@ export const useAuthStore = defineStore('auth', () => {
fetchUserInfo,
loginLoading,
logout,
phoneLogin,
};
});

View File

@ -1,21 +1,26 @@
<script lang="ts" setup>
import type { VbenFormSchema } from '@vben/common-ui';
import type { Recordable } from '@vben/types';
import { getPhoneCodeApi, phoneLoginApi } from '#/api';
import { computed, ref } from 'vue';
import { AuthenticationCodeLogin, z } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { useAuthStore } from '#/store';
defineOptions({ name: 'CodeLogin' });
const loading = ref(false);
const phoneNumber = ref('');
const formSchema = computed((): VbenFormSchema[] => {
return [
{
component: 'VbenInput',
componentProps: {
onChange: (value: any) => {
console.log('Phone number changed:', value.target.value);
phoneNumber.value = value.target.value;
},
placeholder: $t('authentication.mobile'),
},
fieldName: 'phoneNumber',
@ -30,6 +35,10 @@ const formSchema = computed((): VbenFormSchema[] => {
{
component: 'VbenPinInput',
componentProps: {
handleSendCode: async () => {
console.log('Sending code to:', phoneNumber.value);
getPhoneCodeApi(phoneNumber.value);
},
createText: (countdown: number) => {
const text =
countdown > 0
@ -50,16 +59,15 @@ const formSchema = computed((): VbenFormSchema[] => {
* Asynchronously handle the login process
* @param values 登录表单数据
*/
async function handleLogin(values: Recordable<any>) {
// eslint-disable-next-line no-console
useAuthStore().phoneLogin(values)
console.log(values);
}
</script>
<template>
<AuthenticationCodeLogin
:form-schema="formSchema"
:loading="loading"
@submit="handleLogin"
/>
<AuthenticationCodeLogin :form-schema="formSchema" :loading="loading" @submit="handleLogin" />
</template>

View File

@ -30,20 +30,20 @@ const MOCK_USER_OPTIONS: BasicOption[] = [
const formSchema = computed((): VbenFormSchema[] => {
return [
{
component: 'VbenSelect',
componentProps: {
options: MOCK_USER_OPTIONS,
placeholder: $t('authentication.selectAccount'),
},
fieldName: 'selectAccount',
label: $t('authentication.selectAccount'),
rules: z
.string()
.min(1, { message: $t('authentication.selectAccount') })
.optional()
.default('vben'),
},
// {
// component: 'VbenSelect',
// componentProps: {
// options: MOCK_USER_OPTIONS,
// placeholder: $t('authentication.selectAccount'),
// },
// fieldName: 'selectAccount',
// label: $t('authentication.selectAccount'),
// rules: z
// .string()
// .min(1, { message: $t('authentication.selectAccount') })
// .optional()
// .default('vben'),
// },
{
component: 'VbenInput',
componentProps: {
@ -94,5 +94,6 @@ const formSchema = computed((): VbenFormSchema[] => {
:form-schema="formSchema"
:loading="authStore.loginLoading"
@submit="authStore.authLogin"
@codeLoginPath="authStore.authLogin"
/>
</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,17 +1,15 @@
<script lang="ts" setup>
import { useVbenForm, useVbenModal } from '@vben/common-ui';
import dayjs from 'dayjs';
import { addMailApi } from '#/api/core/mail';
import type { MailData } from '#/api/core/mail';
const [Form, FormApi] = useVbenForm({
//
//
commonConfig: {
//
componentProps: {
class: 'w-full h-full',
},
},
// 使 tailwindcss grid
@ -22,176 +20,184 @@ const [Form, FormApi] = useVbenForm({
// labelinput
schema: [
{
component: 'Input',
fieldName: 'Title',
label: '邮件标题',
rules: "required",
component: 'Input',
fieldName: 'Title',
label: '邮件标题',
rules: 'required',
},
{
component: 'Input',
fieldName: 'Subtitle',
label: '邮件副标题',
component: 'Input',
fieldName: 'Subtitle',
label: '邮件副标题',
},
{
component: 'Textarea',
fieldName: 'Content',
label: '邮件内容',
componentProps: {
type: 'textarea',
rows: 8,
},
rules: "required",
component: 'Textarea',
fieldName: 'Content',
label: '邮件内容',
componentProps: {
type: 'textarea',
rows: 8,
},
rules: 'required',
},
{
component: 'Input',
fieldName: 'TitleEN',
label: '英文邮件标题',
rules: "required",
component: 'Input',
fieldName: 'TitleEN',
label: '英文邮件标题',
rules: 'required',
},
{
component: 'Input',
fieldName: 'SubtitleEN',
label: '英文邮件副标题',
component: 'Input',
fieldName: 'SubtitleEN',
label: '英文邮件副标题',
},
{
component: 'Textarea',
fieldName: 'ContentEN',
label: '英文邮件内容',
componentProps: {
type: 'textarea',
rows: 8,
},
rules: "required",
component: 'Textarea',
fieldName: 'ContentEN',
label: '英文邮件内容',
componentProps: {
type: 'textarea',
rows: 8,
},
rules: 'required',
},
{
component: 'Textarea',
fieldName: 'Items',
label: '邮件道具',
componentProps: {
placeholder: '[{Id:1,Num:1},{Id:2,Num:2}]',
type: 'textarea',
rows: 3,
},
component: 'Textarea',
fieldName: 'Items',
label: '邮件道具',
componentProps: {
placeholder: '[{Id:1,Num:1},{Id:2,Num:2}]',
type: 'textarea',
rows: 3,
},
},
{
component: 'RangePicker',
fieldName: 'start_time',
label: '时间区间',
component: 'RangePicker',
fieldName: 'start_time',
label: '时间区间',
},
{
component: 'DatePicker',
fieldName: 'register_time',
label: '注册时间',
component: 'DatePicker',
fieldName: 'register_time',
label: '注册时间',
},
{
component: 'Select',
fieldName: 'mail_type',
defaultValue: 1,
label: '邮件类型',
componentProps: {
options :[
{
label: '节日邮件',
value: 2
},
{
label: '普通邮件',
value:1
}
]
}
component: 'Select',
fieldName: 'mail_type',
defaultValue: 1,
label: '邮件类型',
componentProps: {
options: [
{
label: '节日邮件',
value: 2,
},
{
label: '普通邮件',
value: 1,
},
],
},
},
{
component: 'Select',
fieldName: 'send_type',
defaultValue: 1,
label: '邮件发送类型',
componentProps: {
options :[
{
label: '个人邮件',
value: 2
},
{
label: '全服邮件',
value:1
}
]
}
component: 'Select',
fieldName: 'send_type',
defaultValue: 1,
label: '邮件发送类型',
componentProps: {
options: [
{
label: '个人邮件',
value: 2,
},
{
label: '全服邮件',
value: 1,
},
],
},
},
{
component: 'Textarea',
fieldName: 'ToUids',
label: '玩家uids',
componentProps: {
component: 'Textarea',
fieldName: 'ToUids',
label: '玩家uids',
componentProps: {
disabled: true,
placeholder: 'uid,uid ... 以‘,’分割',
type: 'textarea',
rows: 4,
},
dependencies: {
triggerFields: ['send_type'],
componentProps(values) {
console.log('value' + values.send_type);
if (values.send_type === 2) {
return {
disabled: false,
rules: 'required',
};
}
return {
disabled: true,
placeholder: 'uid,uid ... 以‘,’分割',
type: 'textarea',
rows: 4,
},
dependencies: {
componentProps(values) {
if(values.mail_type === 2) {
return {
disabled: false,
rules: "required",
}
}
return {
disabled: true,
}
},
triggerFields: ['mail_type'],
};
},
},
},
],
})
});
const [Modal, modalApi] = useVbenModal({
confirmText: '提交',
onConfirm: async () => {
//
const values = await FormApi.getValues();
const start_time = values.start_time && values.start_time.length > 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 send_type = values.send_type;
const Title = values.Title;
const Subtitle = values.Subtitle;
const Content = values.Content;
const TitleEN = values.TitleEN;
const SubtitleEN = values.SubtitleEN;
const ContentEN = values.ContentEN;
const Items = values.Items;
const ToUids = values.ToUids;
const modalData = modalApi.getData();
const param: MailData = {
AppId: modalData.AppId,
ServerId: modalData.ServerId,
title: Title,
subtitle: Subtitle,
content: Content,
title_en: TitleEN,
subtitle_en: SubtitleEN,
content_en: ContentEN,
items: Items,
to_uids: ToUids,
start_time: start_time,
end_time: end_time,
register_time: register_time,
mail_type: mail_type,
send_type: send_type,
}
await addMailApi(param);
modalApi.close();
},
})
confirmText: '提交',
onConfirm: async () => {
//
const values = await FormApi.getValues();
const start_time =
values.start_time && values.start_time.length > 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 send_type = values.send_type;
const Title = values.Title;
const Subtitle = values.Subtitle;
const Content = values.Content;
const TitleEN = values.TitleEN;
const SubtitleEN = values.SubtitleEN;
const ContentEN = values.ContentEN;
const Items = values.Items;
const ToUids = values.ToUids;
const modalData = modalApi.getData();
const param: MailData = {
AppId: modalData.AppId,
ServerId: modalData.ServerId,
title: Title,
subtitle: Subtitle,
content: Content,
title_en: TitleEN,
subtitle_en: SubtitleEN,
content_en: ContentEN,
items: Items,
to_uids: ToUids,
start_time: start_time,
end_time: end_time,
register_time: register_time,
mail_type: mail_type,
send_type: send_type,
};
await addMailApi(param);
modalApi.close();
},
});
defineOptions({
name: 'AddMailModal',
})
name: 'AddMailModal',
});
</script>
<template>
<Modal :width="800" title="添加邮件">

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { Page } from '@vben/common-ui';
import { Button, Card, Space, message } from 'ant-design-vue';
import { Button, Card, Space, message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import type { VxeGridListeners, VxeGridProps } from '#/adapter/vxe-table';
import type { VbenFormProps } from '#/adapter/form';
@ -24,8 +24,13 @@ const formOptions: VbenFormProps = {
defaultValue: 1,
componentProps: {
onChange: async (value: number) => {
const serverResponse = await getServerListApi({ AppId: value, Type: 1 });
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
const serverResponse = await getServerListApi({
AppId: value,
Type: 1,
});
ServerList.value = Array.isArray(serverResponse)
? serverResponse
: [];
GridApi.formApi.updateSchema([
{
component: 'Select',
@ -40,9 +45,7 @@ const formOptions: VbenFormProps = {
]);
},
filterOption: true,
options: [
],
options: [],
placeholder: '请选择',
showSearch: true,
},
@ -68,7 +71,7 @@ const formOptions: VbenFormProps = {
submitOnChange: false,
//
submitOnEnter: false,
}
};
const gridOptions: VxeGridProps<MailData> = {
columns: [
{ field: 'mail_id', title: 'id' },
@ -79,25 +82,33 @@ const gridOptions: VxeGridProps<MailData> = {
{ field: 'content', title: '邮件内容' },
{ field: 'content_en', 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: 'create_time', title: '创建时间' },
{slots: { default: 'action' }, title: '操作', width: 100},
{ slots: { default: 'action' }, title: '操作', width: 100 },
],
height: 'auto',
pagerConfig: {},
proxyConfig: {
response: {
total: "total",
result: "data"
total: 'total',
result: 'data',
},
ajax: {
query: async ({page}, formValues) => {
query: async ({ page }, formValues) => {
let AppId = parseInt(formValues.AppId, 10);
return await getMailListApi({
AppId :AppId,
ServerId:formValues.ServerId,
AppId: AppId,
ServerId: formValues.ServerId,
PageSize: page.pageSize,
CurrentPage: page.currentPage,
});
@ -116,12 +127,16 @@ const gridEvents: VxeGridListeners<MailData> = {
// message.info(`cell-click: ${row.title}`);
},
};
const [Grid, GridApi] = useVbenVxeGrid({ gridEvents, formOptions, gridOptions });
const [Grid, GridApi] = useVbenVxeGrid({
gridEvents,
formOptions,
gridOptions,
});
const [AddMailM, AddMailApi] = useVbenModal({
connectedComponent: AddMailModal,
onClosed: async () => {
AddMailApi.close();
GridApi.query()
GridApi.query();
//console.log("close")
},
});
@ -147,7 +162,10 @@ onMounted(async () => {
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 : [];
GridApi.formApi.updateSchema([
{
@ -170,7 +188,7 @@ onMounted(async () => {
async function addMail() {
//console.log('addMail');
const Value = await GridApi.formApi.getValues();
AddMailApi.setData({AppId: Value.AppId, ServerId: Value.ServerId});
AddMailApi.setData({ AppId: Value.AppId, ServerId: Value.ServerId });
AddMailApi.open();
}
@ -178,29 +196,29 @@ async function deleteRow(row: MailData) {
//console.log(row);
GridApi.setLoading(true);
if (typeof row.mail_id == 'number') {
await deleteMailApi(row.AppId, row.ServerId,row.mail_id);
await deleteMailApi(row.AppId, row.ServerId, row.mail_id);
} else {
//console.error('Invalid mail_id:', row.mail_id);
}
GridApi.setLoading(false);
GridApi.query();
}
</script>
<template>
<Page auto-content-height>
<AddMailM class="w-[50%]" />
<AddMailM class="w-[50%]" />
<AddMailM2 class="w-[50%]" />
<Card class="mb-5" title="邮件操作">
<Space>
<Button @click="addMail">新增邮件</Button>
</Space>
</Card>
<Grid >
<Grid>
<template #action="{ row }">
<Button type="link" @click=deleteRow(row) style="color:#cc0000">删除</Button>
<Button type="link" @click="deleteRow(row)" style="color: #cc0000"
>删除</Button
>
</template>
</Grid>
</Page>

View File

@ -9,5 +9,6 @@
}
},
"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<{
submit: [Recordable<any>];
sendCode: [Recordable<any>];
}>();
const router = useRouter();
@ -99,14 +100,9 @@ defineExpose({
</template>
</Title>
<Form />
<VbenButton
:class="{
'cursor-wait': loading,
}"
:loading="loading"
class="w-full"
@click="handleSubmit"
>
<VbenButton :class="{
'cursor-wait': loading,
}" :loading="loading" class="w-full" @click="handleSubmit">
<slot name="submitButtonText">
{{ submitButtonText || $t('common.login') }}
</slot>

View File

@ -165,11 +165,11 @@ defineExpose({
</div>
<!-- 第三方登录 -->
<slot name="third-party-login">
<!-- <slot name="third-party-login">
<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">
{{ $t('authentication.accountTip') }}
<span
@ -179,6 +179,6 @@ defineExpose({
{{ $t('authentication.createAccount') }}
</span>
</div>
</slot>
</slot> -->
</div>
</template>

View File

@ -30,6 +30,10 @@ const formSchema = computed((): VbenFormSchema[] => {
{
component: 'VbenPinInput',
componentProps: {
updated() {
loading.value = false;
console.log('PinInput updated');
},
createText: (countdown: number) => {
const text =
countdown > 0