版本更新
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",
|
||||
"env": { "NODE_ENV": "development" },
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${workspaceFolder}"
|
||||
"webRoot": "${workspaceFolder}/apps/web-antd"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
// workbench
|
||||
"editor.fontFamily": "JetBrains Mono, Menlo, Monaco, 'Courier New', monospace",
|
||||
"workbench.list.smoothScrolling": true,
|
||||
"workbench.startupEditor": "newUntitledFile",
|
||||
"workbench.tree.indent": 10,
|
||||
|
||||
30
apps/web-antd/.vscode/launch.json
vendored
30
apps/web-antd/.vscode/launch.json
vendored
@ -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
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;
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
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>
|
||||
|
||||
@ -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": "升级",
|
||||
|
||||
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',
|
||||
order: -1,
|
||||
title: $t('page.dashboard.title'),
|
||||
authority: ['super'],
|
||||
},
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
|
||||
@ -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,
|
||||
};
|
||||
});
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
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,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({
|
||||
// 水平布局,label和input在同一行
|
||||
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="添加邮件">
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user