版本更新
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
This commit is contained in:
parent
337f15b252
commit
717f5a3c34
11
apps/web-antd/.vscode/launch.json
vendored
11
apps/web-antd/.vscode/launch.json
vendored
@ -11,6 +11,15 @@
|
|||||||
"url": "http://localhost:5666",
|
"url": "http://localhost:5666",
|
||||||
"webRoot": "${workspaceFolder}",
|
"webRoot": "${workspaceFolder}",
|
||||||
"runtimeArgs": ["--do-not-de-elevate"]
|
"runtimeArgs": ["--do-not-de-elevate"]
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"type": "chrome",
|
||||||
|
"name": "vben admin antd dev",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:5666",
|
||||||
|
"env": { "NODE_ENV": "development" },
|
||||||
|
"sourceMaps": true,
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -12,6 +12,9 @@ export interface ActivityData {
|
|||||||
cfg?: string;
|
cfg?: string;
|
||||||
extra?: string;
|
extra?: string;
|
||||||
tag?: string;
|
tag?: string;
|
||||||
|
interval?: number;
|
||||||
|
now_start_time?: number;
|
||||||
|
now_end_time?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActivityListParam {
|
export interface ActivityListParam {
|
||||||
@ -32,6 +35,13 @@ export interface ResgetActivityListApi{
|
|||||||
data: ActivityData[];
|
data: ActivityData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SyncActivityParam {
|
||||||
|
SrcAppId : number;
|
||||||
|
DstAppId : number;
|
||||||
|
}
|
||||||
|
export async function syncActivityApi(p: SyncActivityParam) {
|
||||||
|
return requestClient.post('/activity/sync', p);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getActivityListApi(p:ActivityListParam) {
|
export async function getActivityListApi(p:ActivityListParam) {
|
||||||
return requestClient.post<ResgetActivityListApi>('/activity/list', p);
|
return requestClient.post<ResgetActivityListApi>('/activity/list', p);
|
||||||
@ -41,7 +51,6 @@ export async function editActivityApi(p: EditActivityParam) {
|
|||||||
return requestClient.post('/activity/edit', p);
|
return requestClient.post('/activity/edit', p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function addActivityApi(p: EditActivityParam) {
|
export async function addActivityApi(p: EditActivityParam) {
|
||||||
return requestClient.post('/activity/add', p);
|
return requestClient.post('/activity/add', p);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,19 @@ export interface AdminLogParam {
|
|||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
opration?: string;
|
opration?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AdminConfig {
|
||||||
|
id ?: number;
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdminConfigListParam{
|
||||||
|
page?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getAdminInfoApi(param: UserParam) {
|
export async function getAdminInfoApi(param: UserParam) {
|
||||||
return requestClient.post<UserInfo>('/admin/info', param);
|
return requestClient.post<UserInfo>('/admin/info', param);
|
||||||
}
|
}
|
||||||
@ -26,3 +39,15 @@ export async function getAdminLogListApi(param:AdminLogParam) {
|
|||||||
export async function addAdminApi(param: UserInfo) {
|
export async function addAdminApi(param: UserInfo) {
|
||||||
return requestClient.post<UserInfo>('/admin/add', param);
|
return requestClient.post<UserInfo>('/admin/add', param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getAdminConfigList(param:AdminConfigListParam) {
|
||||||
|
return requestClient.post<AdminConfig>('/admin/config/list', param);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addAdminConfig(param:AdminConfig) {
|
||||||
|
return requestClient.post('/admin/config/add', param);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function editAdminConfig(param:AdminConfig) {
|
||||||
|
return requestClient.post('/admin/config/edit', param);
|
||||||
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ export interface languageParam{
|
|||||||
len_limit?: string;
|
len_limit?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function getStatisticsOrder(data : OperationParam) {
|
export async function getStatisticsOrder(data : OperationParam) {
|
||||||
return requestClient.post('/statistics/order', data);
|
return requestClient.post('/statistics/order', data);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,11 +13,11 @@ defineProps({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-center p-4 border-b border-black pl-12 ml-4 mr-4 mt-2 w-[60%]" style="background: rgb(255, 254, 231);border-radius: 1cm; padding: 1px 10px 1px 10px;justify-content: space-between;">
|
<div class="flex items-center p-4 border-b border-black pl-12 ml-4 mr-4 mt-2 w-[60%]" style="background: rgb(255, 254, 231);border-radius: 1cm; padding: 1px 10px 1px 10px;justify-content: space-between;">
|
||||||
<VbenAvatar :src="'./' + friend.avatarUrl" class="w-12 h-12 rounded-full " />
|
<VbenAvatar :src="'./' + friend.AvatarUrl" class="w-12 h-12 rounded-full " />
|
||||||
<div class="ml-5 w-full">
|
<div class="ml-5 w-full">
|
||||||
<div class="flex flex-row items-center w-full">
|
<div class="flex flex-row items-center w-full">
|
||||||
<h2 class="text-sm font-semibold text-black">{{ friend.NickName }}</h2>
|
<h2 class="text-sm font-semibold text-black">{{ friend.NickName }}</h2>
|
||||||
<Tag class="text-xs ml-2" color="green" v-if="friend.onlineStatus">在线</Tag>
|
<Tag class="text-xs ml-2" color="green" v-if="friend.OnlineStatus">在线</Tag>
|
||||||
<Tag class="text-xs ml-2" color="red" v-else>离线</Tag>
|
<Tag class="text-xs ml-2" color="red" v-else>离线</Tag>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-xs text-gray-500">uid:{{ friend.Uid }}</p>
|
<p class="text-xs text-gray-500">uid:{{ friend.Uid }}</p>
|
||||||
|
|||||||
@ -24,7 +24,8 @@
|
|||||||
"title": "管理中心",
|
"title": "管理中心",
|
||||||
"user": "用户管理",
|
"user": "用户管理",
|
||||||
"setting": "系统设置",
|
"setting": "系统设置",
|
||||||
"log": "操作日志"
|
"log": "操作日志",
|
||||||
|
"config": "配置管理"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"title": "运维管理",
|
"title": "运维管理",
|
||||||
|
|||||||
@ -57,6 +57,8 @@ export interface languageType {
|
|||||||
pt_BR: string;
|
pt_BR: string;
|
||||||
es_LATAM?: string;
|
es_LATAM?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
|
len_limit?: number;
|
||||||
|
character_limit?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -88,9 +90,9 @@ export interface scriptsRecord{
|
|||||||
export interface friendRecord{
|
export interface friendRecord{
|
||||||
Uid: number;
|
Uid: number;
|
||||||
NickName: string;
|
NickName: string;
|
||||||
avatarUrl: string;
|
AvatarUrl: string;
|
||||||
Level: number;
|
Level: number;
|
||||||
LogoutTime?: string;
|
LogoutTime?: string;
|
||||||
LoginTime: string;
|
LoginTime: string;
|
||||||
onlineStatus?: boolean;
|
OnlineStatus?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,17 @@ const routes: RouteRecordRaw[] = [
|
|||||||
title: $t('page.admin.log'),
|
title: $t('page.admin.log'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'UserManagementConfig',
|
||||||
|
path: '/user-management-config',
|
||||||
|
component: () => import('#/views/admin/config/index.vue'),
|
||||||
|
meta: {
|
||||||
|
authority: ['super', 'admin'],
|
||||||
|
affixTab: false,
|
||||||
|
icon: 'material-symbols:assignment-rounded',
|
||||||
|
title: $t('page.admin.config'),
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
26
apps/web-antd/src/store/language.ts
Normal file
26
apps/web-antd/src/store/language.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export const languageLimitData: Record<string, languageLimit> = {
|
||||||
|
'^UI_MergeData_(\\d+)$': { zh_CN: 20, latin:20 },
|
||||||
|
'UI_MainLevelPanel_title': { zh_CN:16, latin: 16 },
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface languageLimit{
|
||||||
|
key?: string;
|
||||||
|
zh_CN?: number;
|
||||||
|
latin?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算字符串长度:中文字符按 2 计,其它字符按 1 计。
|
||||||
|
*/
|
||||||
|
export function getWeightedTextLength(text: string): number {
|
||||||
|
if (!text) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let length = 0;
|
||||||
|
for (const ch of text) {
|
||||||
|
length += /[\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF]/.test(ch) ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
@ -37,4 +37,7 @@ export const activityTypeData: Record<number, string> = {
|
|||||||
5: '买一赠一礼包',
|
5: '买一赠一礼包',
|
||||||
6: '超值加购礼包',
|
6: '超值加购礼包',
|
||||||
7: '好友合作活动',
|
7: '好友合作活动',
|
||||||
|
8: '通行证',
|
||||||
|
9: '锦标赛',
|
||||||
|
10: '猫猫回礼',
|
||||||
};
|
};
|
||||||
@ -117,4 +117,15 @@ export const parseNumber = (param: any) => {
|
|||||||
}catch(e){
|
}catch(e){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const formatJsonStr = (jsonStr: string): string => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(JSON.parse(jsonStr))
|
||||||
|
.replace(/\\r?\\n/g, '')
|
||||||
|
.replace(/\r?\n/g, '');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to format JSON string:', e);
|
||||||
|
return jsonStr;
|
||||||
|
}
|
||||||
|
};
|
||||||
91
apps/web-antd/src/views/admin/config/add-config.vue
Normal file
91
apps/web-antd/src/views/admin/config/add-config.vue
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { addAdminConfig } from '#/api/core/admin.user';
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import type { AdminConfig } from '#/api/core/admin.user';
|
||||||
|
import JsonEitorVue from 'json-editor-vue';
|
||||||
|
import {ref} from 'vue';
|
||||||
|
const config_value = ref();
|
||||||
|
const [Form, FormApi] = useVbenForm({
|
||||||
|
// 所有表单项共用,可单独在表单内覆盖s
|
||||||
|
commonConfig: {
|
||||||
|
// 所有表单项
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 使用 tailwindcss grid布局
|
||||||
|
// 提交函数
|
||||||
|
// 垂直布局,label和input在不同行,值为vertical
|
||||||
|
layout: 'horizontal',
|
||||||
|
// 水平布局,label和input在同一行
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: 'key',
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
fieldName: 'key',
|
||||||
|
rules: 'required',
|
||||||
|
label: 'Key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {},
|
||||||
|
rules: 'required',
|
||||||
|
fieldName: 'value',
|
||||||
|
label: '值',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Textarea',
|
||||||
|
componentProps: {},
|
||||||
|
rules: 'required',
|
||||||
|
fieldName: 'remark',
|
||||||
|
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 cfgjson =
|
||||||
|
typeof config_value.value === 'string'
|
||||||
|
? JSON.parse(config_value.value)
|
||||||
|
: config_value.value;
|
||||||
|
const jsonString = JSON.stringify(cfgjson);
|
||||||
|
const Parms: AdminConfig = {
|
||||||
|
key: values.key,
|
||||||
|
value: jsonString,
|
||||||
|
remark: values.remark,
|
||||||
|
};
|
||||||
|
await addAdminConfig(Parms);
|
||||||
|
modalApi.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
defineOptions({
|
||||||
|
name: 'AddServerModal',
|
||||||
|
});
|
||||||
|
defineExpose({
|
||||||
|
FormApi,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal title="添加配置" :width="800">
|
||||||
|
<Form >
|
||||||
|
<template #value>
|
||||||
|
<JsonEitorVue v-model="config_value" v-bind="{/* local props & attrs */}" class="w-[100%] h-[500px]"/>
|
||||||
|
</template>
|
||||||
|
</Form>>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
88
apps/web-antd/src/views/admin/config/config-table.vue
Normal file
88
apps/web-antd/src/views/admin/config/config-table.vue
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<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 { AdminConfig } from '#/api/core/admin.user';
|
||||||
|
import { getAdminConfigList } from '#/api/core/admin.user';
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import addConfigModal from './add-config.vue';
|
||||||
|
import editConfigModal from './edit-config.vue';
|
||||||
|
const gridOptions: VxeGridProps<AdminConfig> = {
|
||||||
|
columns: [
|
||||||
|
{ field: 'key', title: 'Key', },
|
||||||
|
{ field: 'value', title: '值' },
|
||||||
|
{ field: 'remark', title: '备注', },
|
||||||
|
{title: '操作',width: 200,fixed: 'right',slots: {default:"operation"},},
|
||||||
|
],
|
||||||
|
height: 'auto',
|
||||||
|
pagerConfig: {},
|
||||||
|
proxyConfig: {
|
||||||
|
response: {
|
||||||
|
total: 'total',
|
||||||
|
result: 'data',
|
||||||
|
},
|
||||||
|
ajax: {
|
||||||
|
query: async ( { page }: { page: { pageSize: number; currentPage: number } },
|
||||||
|
formValues: Record<string, any>,) => {
|
||||||
|
return await getAdminConfigList({
|
||||||
|
page: page.currentPage,
|
||||||
|
pageSize: page.pageSize,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
rowConfig: {
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const [Grid, GridApi] = useVbenVxeGrid({ gridOptions });
|
||||||
|
const [addConfigM, addConfigApi] = useVbenModal({
|
||||||
|
connectedComponent: addConfigModal,
|
||||||
|
onClosed: async () => {
|
||||||
|
addConfigApi.close();
|
||||||
|
GridApi.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const addConfig = () => {
|
||||||
|
addConfigApi.open();
|
||||||
|
};
|
||||||
|
const [editConfigM, editConfigApi] = useVbenModal({
|
||||||
|
connectedComponent: editConfigModal,
|
||||||
|
onClosed: async () => {
|
||||||
|
editConfigApi.close();
|
||||||
|
GridApi.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const editConfig = (row: AdminConfig) => {
|
||||||
|
editConfigApi.setData(row);
|
||||||
|
editConfigApi.open();
|
||||||
|
};
|
||||||
|
const deleteConfig = (row: AdminConfig) => {
|
||||||
|
// 这里可以调用删除接口
|
||||||
|
console.log('删除配置', row);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Page auto-content-height class="h-[1200px]">
|
||||||
|
<addConfigM class="w-[50%]" />
|
||||||
|
<editConfigM class="w-[50%]" />
|
||||||
|
<Card class="mb-5" title="配置操作">
|
||||||
|
<Space>
|
||||||
|
<Button @click="addConfig">新增</Button>
|
||||||
|
<Button> 删除 </Button>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
<Grid>
|
||||||
|
<template #operation="{ row }">
|
||||||
|
<Space>
|
||||||
|
<Button type="primary" @click="editConfig(row)">编辑</Button>
|
||||||
|
<Button type="dashed" @click="deleteConfig(row)">删除</Button>
|
||||||
|
</Space>
|
||||||
|
</template>
|
||||||
|
</Grid>>
|
||||||
|
</Page>
|
||||||
|
</template>
|
||||||
104
apps/web-antd/src/views/admin/config/edit-config.vue
Normal file
104
apps/web-antd/src/views/admin/config/edit-config.vue
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { editAdminConfig } from '#/api/core/admin.user';
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { useVbenForm } from '#/adapter/form';
|
||||||
|
import type { AdminConfig } from '#/api/core/admin.user';
|
||||||
|
import JsonEitorVue from 'json-editor-vue';
|
||||||
|
import {ref} from 'vue';
|
||||||
|
const config_value = ref();
|
||||||
|
const config_id = ref<number>();
|
||||||
|
const [Form, FormApi] = useVbenForm({
|
||||||
|
// 所有表单项共用,可单独在表单内覆盖s
|
||||||
|
commonConfig: {
|
||||||
|
// 所有表单项
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 使用 tailwindcss grid布局
|
||||||
|
// 提交函数
|
||||||
|
// 垂直布局,label和input在不同行,值为vertical
|
||||||
|
layout: 'horizontal',
|
||||||
|
// 水平布局,label和input在同一行
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: 'key',
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
fieldName: 'key',
|
||||||
|
rules: 'required',
|
||||||
|
label: 'Key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
componentProps: {},
|
||||||
|
rules: 'required',
|
||||||
|
fieldName: 'value',
|
||||||
|
label: '值',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Textarea',
|
||||||
|
componentProps: {
|
||||||
|
rows: 8,
|
||||||
|
},
|
||||||
|
rules: 'required',
|
||||||
|
fieldName: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
showDefaultActions: false,
|
||||||
|
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||||
|
wrapperClass: 'grid-cols-2',
|
||||||
|
});
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
confirmText: '提交',
|
||||||
|
onOpenChange: () => {
|
||||||
|
const data = modalApi.getData();
|
||||||
|
if (data) {
|
||||||
|
const jsonData = typeof data.value === 'string' ? JSON.parse(data.value) : data.value;
|
||||||
|
config_value.value = jsonData;
|
||||||
|
config_id.value = data.id;
|
||||||
|
FormApi.setValues({
|
||||||
|
key: data.key,
|
||||||
|
remark: data.remark,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onConfirm: async () => {
|
||||||
|
// 提交表单
|
||||||
|
const values = await FormApi.getValues();
|
||||||
|
const cfgjson =
|
||||||
|
typeof config_value.value === 'string'
|
||||||
|
? JSON.parse(config_value.value)
|
||||||
|
: config_value.value;
|
||||||
|
const jsonString = JSON.stringify(cfgjson);
|
||||||
|
const Parms: AdminConfig = {
|
||||||
|
id: config_id.value,
|
||||||
|
key: values.key,
|
||||||
|
value: jsonString,
|
||||||
|
remark: values.remark,
|
||||||
|
};
|
||||||
|
await editAdminConfig(Parms);
|
||||||
|
modalApi.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
defineOptions({
|
||||||
|
name: 'EditConfigModal',
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal title="编辑配置" :width="800">
|
||||||
|
<Form >
|
||||||
|
<template #value>
|
||||||
|
<JsonEitorVue v-model="config_value" v-bind="{/* local props & attrs */}" class="w-[100%] h-[500px]"/>
|
||||||
|
</template>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
6
apps/web-antd/src/views/admin/config/index.vue
Normal file
6
apps/web-antd/src/views/admin/config/index.vue
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import ConfigTable from './config-table.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<ConfigTable />
|
||||||
|
</template>
|
||||||
@ -3,7 +3,6 @@ import { addAdminApi } from '#/api/core/admin.user';
|
|||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { useVbenForm } from '#/adapter/form';
|
import { useVbenForm } from '#/adapter/form';
|
||||||
import type { UserInfo } from '#/model/admin.user';
|
import type { UserInfo } from '#/model/admin.user';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
|
|
||||||
const [Form, FormApi] = useVbenForm({
|
const [Form, FormApi] = useVbenForm({
|
||||||
// 所有表单项共用,可单独在表单内覆盖s
|
// 所有表单项共用,可单独在表单内覆盖s
|
||||||
@ -105,7 +104,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'AddServerModal',
|
name: 'AddUserModal',
|
||||||
});
|
});
|
||||||
defineExpose({
|
defineExpose({
|
||||||
FormApi,
|
FormApi,
|
||||||
@ -113,7 +112,7 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal title="添加服务器" :width="800">
|
<Modal title="添加用户" :width="800">
|
||||||
<Form />
|
<Form />
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -14,7 +14,10 @@ import dayjs from 'dayjs';
|
|||||||
import { AccessControl } from '@vben/access';
|
import { AccessControl } from '@vben/access';
|
||||||
import { useAccess } from '@vben/access';
|
import { useAccess } from '@vben/access';
|
||||||
import GlossaryData from '#/store/glossary.json';
|
import GlossaryData from '#/store/glossary.json';
|
||||||
import { ref } from 'vue';
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { parseNumber } from '#/store/util';
|
||||||
|
import {getWeightedTextLength} from '#/store/language';
|
||||||
|
import type { languageLimit } from '#/store/language';
|
||||||
const { hasAccessByRoles } = useAccess();
|
const { hasAccessByRoles } = useAccess();
|
||||||
const [AddLanguageModal, AddLanguageModalApi] = useVbenModal({
|
const [AddLanguageModal, AddLanguageModalApi] = useVbenModal({
|
||||||
connectedComponent: addLanguage,
|
connectedComponent: addLanguage,
|
||||||
@ -40,6 +43,7 @@ let total: languageType = {
|
|||||||
es_LATAM: '',
|
es_LATAM: '',
|
||||||
};
|
};
|
||||||
const len_limit = ref<string>('');
|
const len_limit = ref<string>('');
|
||||||
|
const language_len_limit = ref<languageLimit[]>([]);
|
||||||
const startDate = dayjs().subtract(7, 'day').startOf('day');
|
const startDate = dayjs().subtract(7, 'day').startOf('day');
|
||||||
const endDate = dayjs().endOf('day');
|
const endDate = dayjs().endOf('day');
|
||||||
const formOptions: VbenFormProps = {
|
const formOptions: VbenFormProps = {
|
||||||
@ -96,9 +100,10 @@ const formOptions: VbenFormProps = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
defaultValue: false,
|
defaultValue: '',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
options: [
|
options: [
|
||||||
|
{ label: 'no_limit', value: '' },
|
||||||
{ label: 'en_US', value: 'en_US' },
|
{ label: 'en_US', value: 'en_US' },
|
||||||
{ label: 'zh_CN', value: 'zh_CN' },
|
{ label: 'zh_CN', value: 'zh_CN' },
|
||||||
{ label: 'pt_BR', value: 'pt_BR' },
|
{ label: 'pt_BR', value: 'pt_BR' },
|
||||||
@ -177,16 +182,16 @@ const gridOptions: VxeGridProps<languageType> = {
|
|||||||
return 'row-green';
|
return 'row-green';
|
||||||
}
|
}
|
||||||
if (len_limit.value === 'zh_CN' && column.field === 'zh_CN') {
|
if (len_limit.value === 'zh_CN' && column.field === 'zh_CN') {
|
||||||
return 'row-yellow';
|
return 'row-blue';
|
||||||
}
|
}
|
||||||
if (len_limit.value === 'en_US' && column.field === 'en_US') {
|
if (len_limit.value === 'en_US' && column.field === 'en_US') {
|
||||||
return 'row-yellow';
|
return 'row-blue';
|
||||||
}
|
}
|
||||||
if (len_limit.value === 'pt_BR' && column.field === 'pt_BR') {
|
if (len_limit.value === 'pt_BR' && column.field === 'pt_BR') {
|
||||||
return 'row-yellow';
|
return 'row-blue';
|
||||||
}
|
}
|
||||||
if (len_limit.value === 'es_LATAM' && column.field === 'es_LATAM') {
|
if (len_limit.value === 'es_LATAM' && column.field === 'es_LATAM') {
|
||||||
return 'row-yellow';
|
return 'row-blue';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -209,13 +214,16 @@ const gridOptions: VxeGridProps<languageType> = {
|
|||||||
len_limit.value = formValues.len_limit || '';
|
len_limit.value = formValues.len_limit || '';
|
||||||
newData = response.data || [];
|
newData = response.data || [];
|
||||||
columnStr = response.column || "";
|
columnStr = response.column || "";
|
||||||
|
language_len_limit.value = response.language_len_limit || [];
|
||||||
if (columnStr && columnStr.length > 0) {
|
if (columnStr && columnStr.length > 0) {
|
||||||
const cols = columnStr.split(',');
|
const cols = columnStr.split(',');
|
||||||
keyVisible = cols.includes('key');
|
keyVisible = cols.includes('key');
|
||||||
en_USVisible = cols.includes('en_US');
|
en_USVisible = cols.includes('en_US');
|
||||||
chineseVisible = cols.includes('zh_CN');
|
chineseVisible = cols.includes('zh_CN');
|
||||||
pt_BRVisible = cols.includes('pt_BR');
|
pt_BRVisible = cols.includes('pt_BR');
|
||||||
|
es_LATAMVisible = cols.includes('es_LATAM');
|
||||||
}
|
}
|
||||||
|
console.log('Column visibility:', { keyVisible, en_USVisible, chineseVisible, pt_BRVisible, es_LATAMVisible });
|
||||||
oldData = newData.map(item => ({ ...item }));
|
oldData = newData.map(item => ({ ...item }));
|
||||||
console.log('API response:', response);
|
console.log('API response:', response);
|
||||||
lastOp = response.op || [];
|
lastOp = response.op || [];
|
||||||
@ -284,8 +292,27 @@ const gridOptions: VxeGridProps<languageType> = {
|
|||||||
} else {
|
} else {
|
||||||
actionVisible = false;
|
actionVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let length = response.data ? response.data.length : 0;
|
let length = response.data ? response.data.length : 0;
|
||||||
|
const list = (response.data || []) as languageType[];
|
||||||
|
list.forEach(item => {
|
||||||
|
if (item.len_limit) {
|
||||||
|
const limit = parseNumber(item.len_limit);
|
||||||
|
if (!isNaN(limit)) {
|
||||||
|
if (len_limit.value === 'zh_CN' && item.zh_CN && item.zh_CN.length > limit) {
|
||||||
|
item.character_limit = `${item.zh_CN.length}/${limit}`;
|
||||||
|
}
|
||||||
|
if (len_limit.value === 'en_US' && item.en_US && item.en_US.length > limit) {
|
||||||
|
item.character_limit = `${item.en_US.length}/${limit}`;
|
||||||
|
}
|
||||||
|
if (len_limit.value === 'pt_BR' && item.pt_BR && item.pt_BR.length > limit) {
|
||||||
|
item.character_limit = `${item.pt_BR.length}/${limit}`;
|
||||||
|
}
|
||||||
|
if (len_limit.value === 'es_LATAM' && item.es_LATAM && item.es_LATAM.length > limit) {
|
||||||
|
item.character_limit = `${item.es_LATAM.length}/${limit}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
GridApi.setGridOptions({
|
GridApi.setGridOptions({
|
||||||
columns: [
|
columns: [
|
||||||
{ editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" }, visible: keyVisible, sortBy: 'key', sortable: true, },
|
{ editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" }, visible: keyVisible, sortBy: 'key', sortable: true, },
|
||||||
@ -304,6 +331,7 @@ const gridOptions: VxeGridProps<languageType> = {
|
|||||||
{ editRender: { name: 'input' }, field: 'pt_BR', title: 'pt_BR', filters: [{ data: "" }], filterRender: { name: "input" }, visible: pt_BRVisible, className: 'en-us-column' },
|
{ editRender: { name: 'input' }, field: 'pt_BR', title: 'pt_BR', filters: [{ data: "" }], filterRender: { name: "input" }, visible: pt_BRVisible, className: 'en-us-column' },
|
||||||
{ editRender: { name: 'input' }, field: 'es_LATAM', title: 'es_LATAM', filters: [{ data: "" }], filterRender: { name: "input" }, visible: es_LATAMVisible, className: 'en-us-column' },
|
{ editRender: { name: 'input' }, field: 'es_LATAM', title: 'es_LATAM', filters: [{ data: "" }], filterRender: { name: "input" }, visible: es_LATAMVisible, className: 'en-us-column' },
|
||||||
{ slots: { default: 'glossary' }, title: 'Glossary', width: 150 },
|
{ slots: { default: 'glossary' }, title: 'Glossary', width: 150 },
|
||||||
|
{ slots: { default: 'character_limit' }, title: 'character_limit', width: 150 },
|
||||||
{ slots: { default: 'action' }, title: $t('page.common.action'), width: 150, visible: actionVisible },
|
{ slots: { default: 'action' }, title: $t('page.common.action'), width: 150, visible: actionVisible },
|
||||||
],
|
],
|
||||||
pagerConfig: {
|
pagerConfig: {
|
||||||
@ -330,6 +358,7 @@ const gridOptions: VxeGridProps<languageType> = {
|
|||||||
keepSource: true,
|
keepSource: true,
|
||||||
keyboardConfig: {
|
keyboardConfig: {
|
||||||
isArrow: true,
|
isArrow: true,
|
||||||
|
isEsc : true,
|
||||||
isTab: true,
|
isTab: true,
|
||||||
isEnter: true,
|
isEnter: true,
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
@ -397,7 +426,21 @@ const gridEvents: VxeGridListeners<languageType> = {
|
|||||||
};
|
};
|
||||||
const [Grid, GridApi] = useVbenVxeGrid({ formOptions, gridOptions, gridEvents });
|
const [Grid, GridApi] = useVbenVxeGrid({ formOptions, gridOptions, gridEvents });
|
||||||
|
|
||||||
|
function handleDocumentClick(e: MouseEvent) {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if (!target.closest('.vxe-body--column')) {
|
||||||
|
GridApi.grid?.clearSelected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('click', handleDocumentClick);
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('click', handleDocumentClick);
|
||||||
|
});
|
||||||
|
|
||||||
function addRow() {
|
function addRow() {
|
||||||
|
GridApi.grid?.clearSelected();
|
||||||
AddLanguageModalApi.open();
|
AddLanguageModalApi.open();
|
||||||
AddLanguageModalApi.setState({
|
AddLanguageModalApi.setState({
|
||||||
title: $t('page.language.newLineContent'),
|
title: $t('page.language.newLineContent'),
|
||||||
@ -451,7 +494,7 @@ function getGlossary(row: languageType) {
|
|||||||
head += ' | es';
|
head += ' | es';
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.entries(GlossaryData).forEach(([key, value]: [string, any]) => {
|
Object.entries(GlossaryData).forEach(([_key, value]: [string, any]) => {
|
||||||
const inGameGlossary = value.term || '';
|
const inGameGlossary = value.term || '';
|
||||||
if (row.en_US && new RegExp(`\\b${inGameGlossary.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i').test(row.en_US)) {
|
if (row.en_US && new RegExp(`\\b${inGameGlossary.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i').test(row.en_US)) {
|
||||||
let content = `${inGameGlossary}`;
|
let content = `${inGameGlossary}`;
|
||||||
@ -476,14 +519,75 @@ function getGlossary(row: languageType) {
|
|||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function characterLimit(row: languageType, columnName : string) {
|
||||||
|
for (const key in language_len_limit.value) {
|
||||||
|
if (language_len_limit.value[key] && new RegExp(language_len_limit.value[key].key || '').test(row.key)) {
|
||||||
|
const limitInfo: languageLimit = language_len_limit.value[key] || {};
|
||||||
|
let limit = 0;
|
||||||
|
let prefix = '';
|
||||||
|
if (columnName === 'zh_CN') {
|
||||||
|
limit = limitInfo.zh_CN || 0;
|
||||||
|
} else {
|
||||||
|
limit = limitInfo.latin || 0;
|
||||||
|
}
|
||||||
|
switch (columnName) {
|
||||||
|
case 'zh_CN':
|
||||||
|
prefix = '中文';
|
||||||
|
break;
|
||||||
|
case 'en_US':
|
||||||
|
prefix = 'English';
|
||||||
|
break;
|
||||||
|
case 'pt_BR':
|
||||||
|
prefix = 'Português';
|
||||||
|
break;
|
||||||
|
case 'es_LATAM':
|
||||||
|
prefix = 'Español';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (limit) {
|
||||||
|
const value = row[columnName as keyof languageType] || '';
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (columnName == 'zh_CN'){
|
||||||
|
return `${prefix}: ${String(getWeightedTextLength(value)/2)}/${limit/2}`;
|
||||||
|
}
|
||||||
|
return `${prefix}: ${String(getWeightedTextLength(value))}/${limit}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function characterLimitColor(row: languageType, columnName : string) {
|
||||||
|
for (const pattern in language_len_limit.value) {
|
||||||
|
if (language_len_limit.value[pattern] && new RegExp(language_len_limit.value[pattern].key || '').test(row.key)) {
|
||||||
|
const limitInfo: languageLimit = language_len_limit.value[pattern] || {};
|
||||||
|
let limit = 0;
|
||||||
|
if (columnName === 'zh_CN') {
|
||||||
|
limit = limitInfo.zh_CN || 0;
|
||||||
|
} else {
|
||||||
|
limit = limitInfo.latin || 0;
|
||||||
|
}
|
||||||
|
if (limit) {
|
||||||
|
const value = row[columnName as keyof languageType] || '';
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const length = getWeightedTextLength(value);
|
||||||
|
if (length > limit) {
|
||||||
|
return 'red';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css">
|
<style lang="css">
|
||||||
.row-green {
|
.row-green {
|
||||||
background-color: #0ea800;
|
background-color: #0ea800;
|
||||||
}
|
}
|
||||||
.row-yellow {
|
.row-blue {
|
||||||
background-color: #c9bc04;
|
background-color: #6281af;
|
||||||
}
|
}
|
||||||
.zh-cn-column {
|
.zh-cn-column {
|
||||||
font-family: "猫啃什锦黑", sans-serif;
|
font-family: "猫啃什锦黑", sans-serif;
|
||||||
@ -515,6 +619,12 @@ function getGlossary(row: languageType) {
|
|||||||
<template #glossary="{ row }">
|
<template #glossary="{ row }">
|
||||||
<span style="font-size: 11px;">{{ getGlossary(row) }} </span>
|
<span style="font-size: 11px;">{{ getGlossary(row) }} </span>
|
||||||
</template>
|
</template>
|
||||||
|
<template #character_limit="{ row }">
|
||||||
|
<span :style="{ fontSize: '11px', color: characterLimitColor(row, 'zh_CN') }">{{ characterLimit(row, 'zh_CN') }}</span><br>
|
||||||
|
<span :style="{ fontSize: '11px', color: characterLimitColor(row, 'en_US') }">{{ characterLimit(row, 'en_US') }}</span><br>
|
||||||
|
<span :style="{ fontSize: '11px', color: characterLimitColor(row, 'pt_BR') }">{{ characterLimit(row, 'pt_BR') }}</span><br>
|
||||||
|
<span :style="{ fontSize: '11px', color: characterLimitColor(row, 'es_LATAM') }">{{ characterLimit(row, 'es_LATAM') }}</span>
|
||||||
|
</template>
|
||||||
<template #action="{ row }">
|
<template #action="{ row }">
|
||||||
<AccessControl :codes="['super', 'admin']" type="role">
|
<AccessControl :codes="['super', 'admin']" type="role">
|
||||||
<Button type="primary" @click="deleteRow(row)">删除</Button>
|
<Button type="primary" @click="deleteRow(row)">删除</Button>
|
||||||
|
|||||||
@ -5,8 +5,9 @@ import type { EditActivityParam } from '#/api/core/activity';
|
|||||||
import { getUnixTime } from '#/store/util';
|
import { getUnixTime } from '#/store/util';
|
||||||
import JsonEitorVue from 'json-editor-vue';
|
import JsonEitorVue from 'json-editor-vue';
|
||||||
import { activityTypeData } from '#/store/order';
|
import { activityTypeData } from '#/store/order';
|
||||||
|
import { parseNumber } from '#/store/util';
|
||||||
import {ref} from 'vue';
|
import {ref} from 'vue';
|
||||||
const value = ref();
|
const value = ref({});
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'DetailMailModal',
|
name: 'DetailMailModal',
|
||||||
});
|
});
|
||||||
@ -63,6 +64,14 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'interval',
|
||||||
|
defaultValue: 0,
|
||||||
|
label: '活动循环间隔(秒)从上次开始时间开始计算,0表示不循环',
|
||||||
|
componentProps: {
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'title',
|
fieldName: 'title',
|
||||||
@ -80,16 +89,6 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
fieldName: 'mail_content',
|
fieldName: 'mail_content',
|
||||||
label: '回收邮件内容',
|
label: '回收邮件内容',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
component: 'Textarea',
|
|
||||||
fieldName: 'cfg',
|
|
||||||
label: '配置',
|
|
||||||
defaultValue: '{}',
|
|
||||||
componentProps: {
|
|
||||||
type: 'textarea',
|
|
||||||
rows: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
component: 'Textarea',
|
component: 'Textarea',
|
||||||
fieldName: 'jsonCfg',
|
fieldName: 'jsonCfg',
|
||||||
@ -113,20 +112,24 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
const values = await FormApi.getValues();
|
const values = await FormApi.getValues();
|
||||||
const mMdata = modalApi.getData();
|
const mMdata = modalApi.getData();
|
||||||
const cfgjson = JSON.parse(values.cfg);
|
const cfgjson =
|
||||||
|
typeof values.cfg === 'string'
|
||||||
|
? JSON.parse(values.cfg)
|
||||||
|
: values.cfg;
|
||||||
const params: EditActivityParam = {
|
const params: EditActivityParam = {
|
||||||
AppId: mMdata.AppId,
|
AppId: mMdata.AppId,
|
||||||
Cfg: {
|
Cfg: {
|
||||||
id: 0,
|
id: 0,
|
||||||
type: values.type,
|
type: parseNumber(values.type),
|
||||||
start_time: values.start_time ? getUnixTime(values.start_time) : 0,
|
start_time: values.start_time ? getUnixTime(values.start_time) : 0,
|
||||||
end_time: values.end_time ? getUnixTime(values.end_time) : 0,
|
end_time: values.end_time ? getUnixTime(values.end_time) : 0,
|
||||||
title: values.title,
|
title: values.title,
|
||||||
level: values.level,
|
level: parseNumber(values.level),
|
||||||
mail_title: values.mail_title,
|
mail_title: values.mail_title,
|
||||||
mail_content: values.mail_content,
|
mail_content: values.mail_content,
|
||||||
cfg: JSON.stringify(cfgjson),
|
cfg: JSON.stringify(cfgjson),
|
||||||
extra: values.extra,
|
extra: values.extra,
|
||||||
|
interval: parseNumber(values.interval),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await addActivityApi(params);
|
await addActivityApi(params);
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { editActivityApi } from '#/api/core/activity';
|
|||||||
import type { EditActivityParam } from '#/api/core/activity';
|
import type { EditActivityParam } from '#/api/core/activity';
|
||||||
import { getUnixTime } from '#/store/util';
|
import { getUnixTime } from '#/store/util';
|
||||||
import JsonEitorVue from 'json-editor-vue';
|
import JsonEitorVue from 'json-editor-vue';
|
||||||
|
import { parseNumber, formatJsonStr } from '#/store/util';
|
||||||
const value = ref();
|
const value = ref();
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'DetailMailModal',
|
name: 'DetailMailModal',
|
||||||
@ -70,6 +70,14 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'interval',
|
||||||
|
defaultValue: 0,
|
||||||
|
label: '活动循环间隔(秒)从上次开始时间开始计算,0表示不循环',
|
||||||
|
componentProps: {
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
fieldName: 'title',
|
fieldName: 'title',
|
||||||
@ -93,7 +101,8 @@ const [Form, FormApi] = useVbenForm({
|
|||||||
label: '配置',
|
label: '配置',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
rows: 8,
|
rows: 12,
|
||||||
|
overflow: 'auto',
|
||||||
},
|
},
|
||||||
rules: 'required',
|
rules: 'required',
|
||||||
},
|
},
|
||||||
@ -123,6 +132,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
mail_content: modalData.mail_content,
|
mail_content: modalData.mail_content,
|
||||||
cfg: modalData.cfg,
|
cfg: modalData.cfg,
|
||||||
extra: modalData.extra,
|
extra: modalData.extra,
|
||||||
|
interval: modalData.interval,
|
||||||
});
|
});
|
||||||
value.value = JSON.parse(modalData.cfg || '{}');
|
value.value = JSON.parse(modalData.cfg || '{}');
|
||||||
console.log('value', value.value);
|
console.log('value', value.value);
|
||||||
@ -132,6 +142,18 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
const mMdata = modalApi.getData();
|
const mMdata = modalApi.getData();
|
||||||
const modalData = mMdata.cfg || {};
|
const modalData = mMdata.cfg || {};
|
||||||
const level: number = Number(values.level) || 0;
|
const level: number = Number(values.level) || 0;
|
||||||
|
const interval: number = Number(values.interval) || 0;
|
||||||
|
let cfgStr = formatJsonStr(value.value);
|
||||||
|
if (typeof value.value === 'string') {
|
||||||
|
try {
|
||||||
|
cfgStr = formatJsonStr(value.value);
|
||||||
|
} catch (e) {
|
||||||
|
message.error('配置必须是合法的JSON字符串');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cfgStr = JSON.stringify(value.value);
|
||||||
|
}
|
||||||
const params: EditActivityParam = {
|
const params: EditActivityParam = {
|
||||||
AppId: mMdata.AppId,
|
AppId: mMdata.AppId,
|
||||||
Cfg: {
|
Cfg: {
|
||||||
@ -143,8 +165,9 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
level: level,
|
level: level,
|
||||||
mail_title: values.mail_title,
|
mail_title: values.mail_title,
|
||||||
mail_content: values.mail_content,
|
mail_content: values.mail_content,
|
||||||
cfg: JSON.stringify(value.value || {}),
|
cfg: cfgStr,
|
||||||
extra: values.extra,
|
extra: values.extra,
|
||||||
|
interval: interval,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const response = await editActivityApi(params);
|
const response = await editActivityApi(params);
|
||||||
@ -157,11 +180,12 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Modal :width="800" title="活动详情">
|
<Modal :width="1200" title="活动详情">
|
||||||
<Form>
|
<Form>
|
||||||
<template #cfg>
|
<template #cfg>
|
||||||
<JsonEitorVue v-model="value" v-bind="{/* local props & attrs */ }" class="w-[100%] h-[500px]" />
|
<JsonEitorVue v-model="value" v-bind="{/* local props & attrs */ }" class="w-[100%] h-[500px]" />
|
||||||
</template>
|
</template>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
128
apps/web-antd/src/views/operation/activity/activity-sync.vue
Normal file
128
apps/web-antd/src/views/operation/activity/activity-sync.vue
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||||
|
import {$t} from '#/locales';
|
||||||
|
import { notification } from 'ant-design-vue';
|
||||||
|
import { syncActivityApi } from '#/api/core/activity';
|
||||||
|
import type { SyncActivityParam } from '#/api/core/activity';
|
||||||
|
defineOptions({
|
||||||
|
name: 'DetailMailModal',
|
||||||
|
});
|
||||||
|
const [Form, FormApi] = useVbenForm({
|
||||||
|
// 所有表单项共用,可单独在表单内覆盖
|
||||||
|
commonConfig: {
|
||||||
|
// 所有表单项
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full h-full',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 使用 tailwindcss grid布局
|
||||||
|
// 提交函数
|
||||||
|
// 垂直布局,label和input在不同行,值为vertical
|
||||||
|
layout: 'horizontal',
|
||||||
|
showDefaultActions: false,
|
||||||
|
// 水平布局,label和input在同一行
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
defaultValue: 1,
|
||||||
|
componentProps: {
|
||||||
|
filterOption: true,
|
||||||
|
options: [],
|
||||||
|
placeholder: '请选择',
|
||||||
|
showSearch: true,
|
||||||
|
},
|
||||||
|
fieldName: 'SrcAppId',
|
||||||
|
label: '源APP:',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
defaultValue: 1,
|
||||||
|
componentProps: {
|
||||||
|
filterOption: true,
|
||||||
|
options: [],
|
||||||
|
placeholder: '请选择',
|
||||||
|
showSearch: true,
|
||||||
|
},
|
||||||
|
fieldName: 'DstAppId',
|
||||||
|
label: '目标APP:',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'label',
|
||||||
|
label: '**',
|
||||||
|
componentProps: {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Input',
|
||||||
|
fieldName: 'ensure',
|
||||||
|
label: '确认框',
|
||||||
|
componentProps: {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
onOpened: async () => {
|
||||||
|
const Value = modalApi.getData();
|
||||||
|
console.log("open", Value)
|
||||||
|
FormApi.updateSchema(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
defaultValue: Value.AppId,
|
||||||
|
componentProps: {
|
||||||
|
filterOption: true,
|
||||||
|
options: Value.AppList ? Value.AppList.map((app: any) => ({ label: $t('page.server.' + app.AppName), value: app.AppId })) : [],
|
||||||
|
placeholder: '请选择',
|
||||||
|
showSearch: true,
|
||||||
|
},
|
||||||
|
fieldName: 'SrcAppId',
|
||||||
|
label: '源APP:',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: 'Select',
|
||||||
|
defaultValue: 1,
|
||||||
|
componentProps: {
|
||||||
|
filterOption: true,
|
||||||
|
options: Value.AppList ? Value.AppList.map((app: any) => ({ label: $t('page.server.' + app.AppName), value: app.AppId })) : [],
|
||||||
|
placeholder: '请选择',
|
||||||
|
showSearch: true,
|
||||||
|
},
|
||||||
|
fieldName: 'DstAppId',
|
||||||
|
label: '目标APP:',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmText: '提交',
|
||||||
|
onConfirm: async () => {
|
||||||
|
const values = await FormApi.getValues();
|
||||||
|
if (values.ensure !== 'meowment') {
|
||||||
|
// eslint-disable-next-line no-alert
|
||||||
|
notification.error({ message: '请输入meowment进行确认' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const params: SyncActivityParam = {
|
||||||
|
SrcAppId: values.SrcAppId,
|
||||||
|
DstAppId: values.DstAppId,
|
||||||
|
};
|
||||||
|
const res = await syncActivityApi(params);
|
||||||
|
if (res.code === 0) {
|
||||||
|
notification.success({ message: '同步成功' });
|
||||||
|
} else {
|
||||||
|
notification.error({ message: '同步失败:' + res.message });
|
||||||
|
}
|
||||||
|
modalApi.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Modal :width="800" title="同步活动配置">
|
||||||
|
<Form>
|
||||||
|
<template #label>
|
||||||
|
<span>请输入<span style="color: red;">meowment</span>进行确认</span>
|
||||||
|
</template>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
@ -12,6 +12,7 @@ import { useVbenModal } from '@vben/common-ui';
|
|||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import AddActivityModal from './activity-add.vue';
|
import AddActivityModal from './activity-add.vue';
|
||||||
import DetailActivityModal from './activity-detail.vue';
|
import DetailActivityModal from './activity-detail.vue';
|
||||||
|
import SyncActivityModal from './activity-sync.vue';
|
||||||
import { activityTypeData } from '#/store/order';
|
import { activityTypeData } from '#/store/order';
|
||||||
import { parseNumber } from '#/store/util';
|
import { parseNumber } from '#/store/util';
|
||||||
import { $t } from '#/locales'
|
import { $t } from '#/locales'
|
||||||
@ -65,8 +66,9 @@ const gridOptions: VxeGridProps<ActivityData> = {
|
|||||||
{ field: 'id', title: 'id' },
|
{ field: 'id', title: 'id' },
|
||||||
{ field: 'type', title: '活动类型', formatter: ({ cellValue }) => activityTypeData[cellValue] || cellValue },
|
{ field: 'type', title: '活动类型', formatter: ({ cellValue }) => activityTypeData[cellValue] || cellValue },
|
||||||
{ field: 'level', title: '开启等级', },
|
{ field: 'level', title: '开启等级', },
|
||||||
{ field: 'start_time', title: '开启时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString(), },
|
{ field: 'now_start_time', title: '开启时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString(), },
|
||||||
{ field: 'end_time', title: '结束时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString(), },
|
{ field: 'now_end_time', title: '结束时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString(), },
|
||||||
|
{ field: 'interval', title: '活动循环间隔(秒)从上次开始时间开始计算,0表示不循环', },
|
||||||
{ field: 'tag', title: '状态', slots: { default: 'tag' } },
|
{ field: 'tag', title: '状态', slots: { default: 'tag' } },
|
||||||
],
|
],
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
@ -95,9 +97,27 @@ const gridOptions: VxeGridProps<ActivityData> = {
|
|||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Math.floor(Date.now() / 1000);
|
||||||
activityList.value = [];
|
activityList.value = [];
|
||||||
for (let item of response.data) {
|
for (let item of response.data) {
|
||||||
if (item.end_time < now) {
|
const interval = item.interval || 0;
|
||||||
|
if (interval == 0) {
|
||||||
|
item.now_start_time = item.start_time;
|
||||||
|
item.now_end_time = item.end_time;
|
||||||
|
} else {
|
||||||
|
if (now < item.start_time) {
|
||||||
|
item.now_start_time = item.start_time;
|
||||||
|
item.now_end_time = item.end_time;
|
||||||
|
} else {
|
||||||
|
const cycle = Math.floor((now - item.start_time) / interval);
|
||||||
|
item.now_start_time = item.start_time + cycle * interval;
|
||||||
|
item.now_end_time = item.end_time + cycle * interval;
|
||||||
|
if (item.now_end_time < now) {
|
||||||
|
item.now_start_time += interval;
|
||||||
|
item.now_end_time += interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.now_end_time < now) {
|
||||||
item.tag = '已结束';
|
item.tag = '已结束';
|
||||||
} else if (item.start_time > now) {
|
} else if (item.now_start_time > now) {
|
||||||
item.tag = '未开始';
|
item.tag = '未开始';
|
||||||
} else {
|
} else {
|
||||||
activityList.value = Array.from(new Set([...activityList.value, activityTypeData[item.type]])).filter((v): v is string => v !== undefined);
|
activityList.value = Array.from(new Set([...activityList.value, activityTypeData[item.type]])).filter((v): v is string => v !== undefined);
|
||||||
@ -137,6 +157,20 @@ const [AddActivityM, AddActivityApi] = useVbenModal({
|
|||||||
|
|
||||||
const [DetailActivityM, DetailActivityApi] = useVbenModal({
|
const [DetailActivityM, DetailActivityApi] = useVbenModal({
|
||||||
connectedComponent: DetailActivityModal,
|
connectedComponent: DetailActivityModal,
|
||||||
|
onClosed: async () => {
|
||||||
|
DetailActivityApi.close();
|
||||||
|
GridApi.query();
|
||||||
|
//console.log("close")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const [SyncActivityM, SyncActivityApi] = useVbenModal({
|
||||||
|
connectedComponent: SyncActivityModal,
|
||||||
|
onClosed: async () => {
|
||||||
|
SyncActivityApi.close();
|
||||||
|
GridApi.query();
|
||||||
|
//console.log("close")
|
||||||
|
}
|
||||||
});
|
});
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
@ -199,12 +233,19 @@ async function addActivity() {
|
|||||||
AddActivityApi.open();
|
AddActivityApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function syncCfg(){
|
||||||
|
const Value = await GridApi.formApi.getValues();
|
||||||
|
SyncActivityApi.setData({ AppList: appList.value, AppId: Value.AppId, ServerId: Value.ServerId });
|
||||||
|
SyncActivityApi.open();
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height class="h-[800px]">
|
<Page auto-content-height class="h-[800px]">
|
||||||
<AddActivityM class="w-[50%]" />
|
<AddActivityM class="w-[50%]" />
|
||||||
<DetailActivityM class="w-[50%]" />
|
<DetailActivityM class="w-[50%]" />
|
||||||
|
<SyncActivityM class="w-[50%]" />
|
||||||
<Card class="mb-5" title="活动操作">
|
<Card class="mb-5" title="活动操作">
|
||||||
<div class="mb-5">
|
<div class="mb-5">
|
||||||
<Space>
|
<Space>
|
||||||
@ -215,9 +256,11 @@ async function addActivity() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Space>
|
<Space>
|
||||||
<Button @click="addActivity">新增活动</Button>
|
<Button type="primary" @click="addActivity">新增活动</Button>
|
||||||
|
</Space>
|
||||||
|
<Space class="ml-5">
|
||||||
|
<Button type="primary" @click="syncCfg">同步活动</Button>
|
||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
</Card>
|
</Card>
|
||||||
<Grid>
|
<Grid>
|
||||||
<template #tag="{ row }">
|
<template #tag="{ row }">
|
||||||
|
|||||||
@ -1,258 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { Page } from '@vben/common-ui';
|
|
||||||
import { Button, Card, Space, Image, Tag } from 'ant-design-vue';
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
|
||||||
import type { VxeGridListeners, VxeGridProps } from '#/adapter/vxe-table';
|
|
||||||
import type { VbenFormProps } from '#/adapter/form';
|
|
||||||
import type { MailData } from '#/api/core/mail';
|
|
||||||
import { getServerListApi, getAppListApi } from '#/api/core/server';
|
|
||||||
import { getMailListApi, deleteMailApi } from '#/api/core/mail';
|
|
||||||
import type { AppData, ServerData } from '#/api/core/server';
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
|
||||||
import { onMounted, ref } from 'vue';
|
|
||||||
import AddMailModal from './activity-add.vue';
|
|
||||||
import DetailMailModal from './activity-detail.vue';
|
|
||||||
import { getItemUrl } from '#/store/util';
|
|
||||||
const appList = ref<AppData[]>([]);
|
|
||||||
const ServerList = ref<ServerData[]>([]);
|
|
||||||
const formOptions: VbenFormProps = {
|
|
||||||
// 默认展开
|
|
||||||
collapsed: false,
|
|
||||||
schema: [
|
|
||||||
{
|
|
||||||
component: 'Select',
|
|
||||||
defaultValue: 1,
|
|
||||||
componentProps: {
|
|
||||||
onChange: async (value: number) => {
|
|
||||||
const serverResponse = await getServerListApi({
|
|
||||||
AppId: value,
|
|
||||||
Type: 1,
|
|
||||||
});
|
|
||||||
ServerList.value = Array.isArray(serverResponse)
|
|
||||||
? serverResponse
|
|
||||||
: [];
|
|
||||||
GridApi.formApi.updateSchema([
|
|
||||||
{
|
|
||||||
component: 'Select',
|
|
||||||
componentProps: {
|
|
||||||
options: ServerList.value.map((item) => ({
|
|
||||||
label: item.ServerName,
|
|
||||||
value: item.ServerId,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
fieldName: 'ServerId',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
filterOption: true,
|
|
||||||
options: [],
|
|
||||||
placeholder: '请选择',
|
|
||||||
showSearch: true,
|
|
||||||
},
|
|
||||||
fieldName: 'AppId',
|
|
||||||
label: 'APP:',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: 'Select',
|
|
||||||
defaultValue: 1,
|
|
||||||
componentProps: {
|
|
||||||
options: [],
|
|
||||||
},
|
|
||||||
fieldName: 'ServerId',
|
|
||||||
label: '区服',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
// 控制表单是否显示折叠按钮
|
|
||||||
showCollapseButton: true,
|
|
||||||
submitButtonOptions: {
|
|
||||||
content: '查询',
|
|
||||||
},
|
|
||||||
// 是否在字段值改变时提交表单
|
|
||||||
submitOnChange: false,
|
|
||||||
// 按下回车时是否提交表单
|
|
||||||
submitOnEnter: false,
|
|
||||||
};
|
|
||||||
const gridOptions: VxeGridProps<MailData> = {
|
|
||||||
columns: [
|
|
||||||
{ field: 'mail_id', title: 'id' },
|
|
||||||
{ field: 'title', title: '邮件标题' },
|
|
||||||
// { field: 'title_en', title: '英文邮件标题' },
|
|
||||||
{ field: 'subtitle', title: '邮件副标题' },
|
|
||||||
// { field: 'subtitleEN', title: '英文邮件副标题' },
|
|
||||||
{ field: 'content', title: '邮件内容' },
|
|
||||||
// { field: 'content_en', title: '英文邮件内容' },
|
|
||||||
{ field: 'items', title: '道具', slots: { default: 'items' } },
|
|
||||||
{
|
|
||||||
field: 'start_time',
|
|
||||||
title: '开始时间',
|
|
||||||
formatter: ({ cellValue }) =>
|
|
||||||
cellValue ? new Date(cellValue * 1000).toLocaleString() : '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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 },
|
|
||||||
],
|
|
||||||
height: 'auto',
|
|
||||||
pagerConfig: {},
|
|
||||||
proxyConfig: {
|
|
||||||
response: {
|
|
||||||
total: 'total',
|
|
||||||
result: 'data',
|
|
||||||
},
|
|
||||||
ajax: {
|
|
||||||
query: async ({ page }, formValues) => {
|
|
||||||
let AppId = parseInt(formValues.AppId, 10);
|
|
||||||
return await getMailListApi({
|
|
||||||
AppId: AppId,
|
|
||||||
ServerId: formValues.ServerId,
|
|
||||||
PageSize: page.pageSize,
|
|
||||||
CurrentPage: page.currentPage,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
showOverflow: false,
|
|
||||||
rowConfig: {
|
|
||||||
isHover: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const gridEvents: VxeGridListeners<MailData> = {
|
|
||||||
cellClick: ({ row }) => {
|
|
||||||
AddMailApi2.setData(row);
|
|
||||||
AddMailApi2.open();
|
|
||||||
// message.info(`cell-click: ${row.title}`);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const [Grid, GridApi] = useVbenVxeGrid({
|
|
||||||
gridEvents,
|
|
||||||
formOptions,
|
|
||||||
gridOptions,
|
|
||||||
});
|
|
||||||
const [AddMailM, AddMailApi] = useVbenModal({
|
|
||||||
connectedComponent: AddMailModal,
|
|
||||||
onClosed: async () => {
|
|
||||||
AddMailApi.close();
|
|
||||||
GridApi.query();
|
|
||||||
//console.log("close")
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const [AddMailM2, AddMailApi2] = useVbenModal({
|
|
||||||
connectedComponent: DetailMailModal,
|
|
||||||
});
|
|
||||||
onMounted(async () => {
|
|
||||||
try {
|
|
||||||
const response = await getAppListApi();
|
|
||||||
appList.value = Array.isArray(response) ? response : [];
|
|
||||||
const app = appList.value[0];
|
|
||||||
if (!app) return;
|
|
||||||
GridApi.formApi.updateSchema([
|
|
||||||
{
|
|
||||||
component: 'Select',
|
|
||||||
componentProps: {
|
|
||||||
options: appList.value.map((item) => ({
|
|
||||||
label: item.AppName,
|
|
||||||
value: item.AppId,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
fieldName: 'AppId',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const serverResponse = await getServerListApi({
|
|
||||||
AppId: app.AppId,
|
|
||||||
Type: 1,
|
|
||||||
});
|
|
||||||
ServerList.value = Array.isArray(serverResponse) ? serverResponse : [];
|
|
||||||
GridApi.formApi.updateSchema([
|
|
||||||
{
|
|
||||||
component: 'Select',
|
|
||||||
componentProps: {
|
|
||||||
options: ServerList.value.map((item) => ({
|
|
||||||
label: item.ServerName,
|
|
||||||
value: item.ServerId,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
fieldName: 'ServerId',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
} catch (e) {
|
|
||||||
appList.value = [];
|
|
||||||
//console.log(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function addMail() {
|
|
||||||
//console.log('addMail');
|
|
||||||
const Value = await GridApi.formApi.getValues();
|
|
||||||
AddMailApi.setData({ AppId: Value.AppId, ServerId: Value.ServerId });
|
|
||||||
AddMailApi.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
//console.error('Invalid mail_id:', row.mail_id);
|
|
||||||
}
|
|
||||||
GridApi.setLoading(false);
|
|
||||||
GridApi.query();
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromatItems(items: string) {
|
|
||||||
try {
|
|
||||||
const itemList = JSON.parse(items);
|
|
||||||
const r = itemList.map((item: { Id: number; Num: number }) => ({
|
|
||||||
...item,
|
|
||||||
url: getItemUrl(item.Id),
|
|
||||||
}));
|
|
||||||
return r;
|
|
||||||
} catch (e) {
|
|
||||||
//console.error('Failed to parse items:', e);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Page auto-content-height class="h-[800px]">
|
|
||||||
<AddMailM class="w-[50%]" />
|
|
||||||
<AddMailM2 class="w-[50%]" />
|
|
||||||
<Card class="mb-5" title="邮件操作">
|
|
||||||
<Space>
|
|
||||||
<Button @click="addMail">新增邮件</Button>
|
|
||||||
</Space>
|
|
||||||
</Card>
|
|
||||||
<Grid>
|
|
||||||
<template #items="{row}">
|
|
||||||
<div class="flex flex-wrap items-center justify-center">
|
|
||||||
<div v-for="item in fromatItems(row.items)" :key="item.Id" class="flex items-center gap-1">
|
|
||||||
<template v-if="item.url">
|
|
||||||
<Image :src="item.url" width="30px" />
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<Tag class="w-[45px] h-[30px] flex items-center justify-center text-xs">
|
|
||||||
{{item.Id}}
|
|
||||||
</Tag>
|
|
||||||
</template>
|
|
||||||
<span>x{{ item.Num }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #action="{ row }">
|
|
||||||
<Button type="link" @click="deleteRow(row)" style="color: #cc0000">删除</Button>
|
|
||||||
</template>
|
|
||||||
</Grid>
|
|
||||||
</Page>
|
|
||||||
</template>
|
|
||||||
@ -152,7 +152,7 @@ const gridOptions: VxeGridProps<RowType> = {
|
|||||||
{ field: 'change_num', title: '变化数值', align: 'center', width: 120 },
|
{ field: 'change_num', title: '变化数值', align: 'center', width: 120 },
|
||||||
{ field: 'change_after', title: '变化后数值', align: 'center' },
|
{ field: 'change_after', title: '变化后数值', align: 'center' },
|
||||||
{ field: 'change_reason', title: '原因', align: 'center' },
|
{ field: 'change_reason', title: '原因', align: 'center' },
|
||||||
{ field: 'item_id', title: '道具名称', formatter: ({ cellValue }) => formatItemName(cellValue), align: 'center' },
|
{ field: 'item_name', title: '道具名称', align: 'center' },
|
||||||
{ field: 'timestamp', title: '时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString(), align: 'center' },
|
{ field: 'timestamp', title: '时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString(), align: 'center' },
|
||||||
],
|
],
|
||||||
stripe: true,
|
stripe: true,
|
||||||
|
|||||||
@ -316,6 +316,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
info.value.Diamond = r.Diamond;
|
info.value.Diamond = r.Diamond;
|
||||||
info.value.Energy = r.Energy;
|
info.value.Energy = r.Energy;
|
||||||
info.value.Mac = r.Mac;
|
info.value.Mac = r.Mac;
|
||||||
|
r.TodayCumulative = r.TodayCumulative || 0;
|
||||||
info.value.Cumulative = (r.Cumulative / 3600).toFixed(2) + 'h';
|
info.value.Cumulative = (r.Cumulative / 3600).toFixed(2) + 'h';
|
||||||
info.value.TodayCumulative =
|
info.value.TodayCumulative =
|
||||||
(r.TodayCumulative / 3600).toFixed(2) + 'h';
|
(r.TodayCumulative / 3600).toFixed(2) + 'h';
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user