版本更新
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",
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"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;
|
||||
extra?: string;
|
||||
tag?: string;
|
||||
interval?: number;
|
||||
now_start_time?: number;
|
||||
now_end_time?: number;
|
||||
}
|
||||
|
||||
export interface ActivityListParam {
|
||||
@ -32,6 +35,13 @@ export interface ResgetActivityListApi{
|
||||
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) {
|
||||
return requestClient.post<ResgetActivityListApi>('/activity/list', p);
|
||||
@ -41,7 +51,6 @@ export async function editActivityApi(p: EditActivityParam) {
|
||||
return requestClient.post('/activity/edit', p);
|
||||
}
|
||||
|
||||
|
||||
export async function addActivityApi(p: EditActivityParam) {
|
||||
return requestClient.post('/activity/add', p);
|
||||
}
|
||||
|
||||
@ -10,6 +10,19 @@ export interface AdminLogParam {
|
||||
pageSize?: number;
|
||||
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) {
|
||||
return requestClient.post<UserInfo>('/admin/info', param);
|
||||
}
|
||||
@ -26,3 +39,15 @@ export async function getAdminLogListApi(param:AdminLogParam) {
|
||||
export async function addAdminApi(param: UserInfo) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
export async function getStatisticsOrder(data : OperationParam) {
|
||||
return requestClient.post('/statistics/order', data);
|
||||
}
|
||||
|
||||
@ -13,11 +13,11 @@ defineProps({
|
||||
|
||||
<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;">
|
||||
<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="flex flex-row items-center w-full">
|
||||
<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>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500">uid:{{ friend.Uid }}</p>
|
||||
|
||||
@ -24,7 +24,8 @@
|
||||
"title": "管理中心",
|
||||
"user": "用户管理",
|
||||
"setting": "系统设置",
|
||||
"log": "操作日志"
|
||||
"log": "操作日志",
|
||||
"config": "配置管理"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "运维管理",
|
||||
|
||||
@ -57,6 +57,8 @@ export interface languageType {
|
||||
pt_BR: string;
|
||||
es_LATAM?: string;
|
||||
url?: string;
|
||||
len_limit?: number;
|
||||
character_limit?: string;
|
||||
}
|
||||
|
||||
|
||||
@ -88,9 +90,9 @@ export interface scriptsRecord{
|
||||
export interface friendRecord{
|
||||
Uid: number;
|
||||
NickName: string;
|
||||
avatarUrl: string;
|
||||
AvatarUrl: string;
|
||||
Level: number;
|
||||
LogoutTime?: string;
|
||||
LoginTime: string;
|
||||
onlineStatus?: boolean;
|
||||
OnlineStatus?: boolean;
|
||||
}
|
||||
|
||||
@ -37,6 +37,17 @@ const routes: RouteRecordRaw[] = [
|
||||
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: '买一赠一礼包',
|
||||
6: '超值加购礼包',
|
||||
7: '好友合作活动',
|
||||
8: '通行证',
|
||||
9: '锦标赛',
|
||||
10: '猫猫回礼',
|
||||
};
|
||||
@ -118,3 +118,14 @@ export const parseNumber = (param: any) => {
|
||||
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 { useVbenForm } from '#/adapter/form';
|
||||
import type { UserInfo } from '#/model/admin.user';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const [Form, FormApi] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖s
|
||||
@ -105,7 +104,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
},
|
||||
});
|
||||
defineOptions({
|
||||
name: 'AddServerModal',
|
||||
name: 'AddUserModal',
|
||||
});
|
||||
defineExpose({
|
||||
FormApi,
|
||||
@ -113,7 +112,7 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="添加服务器" :width="800">
|
||||
<Modal title="添加用户" :width="800">
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@ -14,7 +14,10 @@ import dayjs from 'dayjs';
|
||||
import { AccessControl } from '@vben/access';
|
||||
import { useAccess } from '@vben/access';
|
||||
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 [AddLanguageModal, AddLanguageModalApi] = useVbenModal({
|
||||
connectedComponent: addLanguage,
|
||||
@ -40,6 +43,7 @@ let total: languageType = {
|
||||
es_LATAM: '',
|
||||
};
|
||||
const len_limit = ref<string>('');
|
||||
const language_len_limit = ref<languageLimit[]>([]);
|
||||
const startDate = dayjs().subtract(7, 'day').startOf('day');
|
||||
const endDate = dayjs().endOf('day');
|
||||
const formOptions: VbenFormProps = {
|
||||
@ -96,9 +100,10 @@ const formOptions: VbenFormProps = {
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
defaultValue: false,
|
||||
defaultValue: '',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: 'no_limit', value: '' },
|
||||
{ label: 'en_US', value: 'en_US' },
|
||||
{ label: 'zh_CN', value: 'zh_CN' },
|
||||
{ label: 'pt_BR', value: 'pt_BR' },
|
||||
@ -177,16 +182,16 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
return 'row-green';
|
||||
}
|
||||
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') {
|
||||
return 'row-yellow';
|
||||
return 'row-blue';
|
||||
}
|
||||
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') {
|
||||
return 'row-yellow';
|
||||
return 'row-blue';
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -209,13 +214,16 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
len_limit.value = formValues.len_limit || '';
|
||||
newData = response.data || [];
|
||||
columnStr = response.column || "";
|
||||
language_len_limit.value = response.language_len_limit || [];
|
||||
if (columnStr && columnStr.length > 0) {
|
||||
const cols = columnStr.split(',');
|
||||
keyVisible = cols.includes('key');
|
||||
en_USVisible = cols.includes('en_US');
|
||||
chineseVisible = cols.includes('zh_CN');
|
||||
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 }));
|
||||
console.log('API response:', response);
|
||||
lastOp = response.op || [];
|
||||
@ -284,8 +292,27 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
} else {
|
||||
actionVisible = false;
|
||||
}
|
||||
|
||||
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({
|
||||
columns: [
|
||||
{ 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: '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: 'character_limit' }, title: 'character_limit', width: 150 },
|
||||
{ slots: { default: 'action' }, title: $t('page.common.action'), width: 150, visible: actionVisible },
|
||||
],
|
||||
pagerConfig: {
|
||||
@ -330,6 +358,7 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
keepSource: true,
|
||||
keyboardConfig: {
|
||||
isArrow: true,
|
||||
isEsc : true,
|
||||
isTab: true,
|
||||
isEnter: true,
|
||||
isEdit: true,
|
||||
@ -397,7 +426,21 @@ const gridEvents: VxeGridListeners<languageType> = {
|
||||
};
|
||||
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() {
|
||||
GridApi.grid?.clearSelected();
|
||||
AddLanguageModalApi.open();
|
||||
AddLanguageModalApi.setState({
|
||||
title: $t('page.language.newLineContent'),
|
||||
@ -451,7 +494,7 @@ function getGlossary(row: languageType) {
|
||||
head += ' | es';
|
||||
}
|
||||
|
||||
Object.entries(GlossaryData).forEach(([key, value]: [string, any]) => {
|
||||
Object.entries(GlossaryData).forEach(([_key, value]: [string, any]) => {
|
||||
const inGameGlossary = value.term || '';
|
||||
if (row.en_US && new RegExp(`\\b${inGameGlossary.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i').test(row.en_US)) {
|
||||
let content = `${inGameGlossary}`;
|
||||
@ -476,14 +519,75 @@ function getGlossary(row: languageType) {
|
||||
}
|
||||
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>
|
||||
|
||||
<style lang="css">
|
||||
.row-green {
|
||||
background-color: #0ea800;
|
||||
}
|
||||
.row-yellow {
|
||||
background-color: #c9bc04;
|
||||
.row-blue {
|
||||
background-color: #6281af;
|
||||
}
|
||||
.zh-cn-column {
|
||||
font-family: "猫啃什锦黑", sans-serif;
|
||||
@ -515,6 +619,12 @@ function getGlossary(row: languageType) {
|
||||
<template #glossary="{ row }">
|
||||
<span style="font-size: 11px;">{{ getGlossary(row) }} </span>
|
||||
</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 }">
|
||||
<AccessControl :codes="['super', 'admin']" type="role">
|
||||
<Button type="primary" @click="deleteRow(row)">删除</Button>
|
||||
|
||||
@ -5,8 +5,9 @@ import type { EditActivityParam } from '#/api/core/activity';
|
||||
import { getUnixTime } from '#/store/util';
|
||||
import JsonEitorVue from 'json-editor-vue';
|
||||
import { activityTypeData } from '#/store/order';
|
||||
import { parseNumber } from '#/store/util';
|
||||
import {ref} from 'vue';
|
||||
const value = ref();
|
||||
const value = ref({});
|
||||
defineOptions({
|
||||
name: 'DetailMailModal',
|
||||
});
|
||||
@ -63,6 +64,14 @@ const [Form, FormApi] = useVbenForm({
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'interval',
|
||||
defaultValue: 0,
|
||||
label: '活动循环间隔(秒)从上次开始时间开始计算,0表示不循环',
|
||||
componentProps: {
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'title',
|
||||
@ -80,16 +89,6 @@ const [Form, FormApi] = useVbenForm({
|
||||
fieldName: 'mail_content',
|
||||
label: '回收邮件内容',
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
fieldName: 'cfg',
|
||||
label: '配置',
|
||||
defaultValue: '{}',
|
||||
componentProps: {
|
||||
type: 'textarea',
|
||||
rows: 8,
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
fieldName: 'jsonCfg',
|
||||
@ -113,20 +112,24 @@ const [Modal, modalApi] = useVbenModal({
|
||||
onConfirm: async () => {
|
||||
const values = await FormApi.getValues();
|
||||
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 = {
|
||||
AppId: mMdata.AppId,
|
||||
Cfg: {
|
||||
id: 0,
|
||||
type: values.type,
|
||||
type: parseNumber(values.type),
|
||||
start_time: values.start_time ? getUnixTime(values.start_time) : 0,
|
||||
end_time: values.end_time ? getUnixTime(values.end_time) : 0,
|
||||
title: values.title,
|
||||
level: values.level,
|
||||
level: parseNumber(values.level),
|
||||
mail_title: values.mail_title,
|
||||
mail_content: values.mail_content,
|
||||
cfg: JSON.stringify(cfgjson),
|
||||
extra: values.extra,
|
||||
interval: parseNumber(values.interval),
|
||||
},
|
||||
};
|
||||
await addActivityApi(params);
|
||||
|
||||
@ -8,7 +8,7 @@ import { editActivityApi } from '#/api/core/activity';
|
||||
import type { EditActivityParam } from '#/api/core/activity';
|
||||
import { getUnixTime } from '#/store/util';
|
||||
import JsonEitorVue from 'json-editor-vue';
|
||||
|
||||
import { parseNumber, formatJsonStr } from '#/store/util';
|
||||
const value = ref();
|
||||
defineOptions({
|
||||
name: 'DetailMailModal',
|
||||
@ -70,6 +70,14 @@ const [Form, FormApi] = useVbenForm({
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'interval',
|
||||
defaultValue: 0,
|
||||
label: '活动循环间隔(秒)从上次开始时间开始计算,0表示不循环',
|
||||
componentProps: {
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'title',
|
||||
@ -93,7 +101,8 @@ const [Form, FormApi] = useVbenForm({
|
||||
label: '配置',
|
||||
componentProps: {
|
||||
type: 'textarea',
|
||||
rows: 8,
|
||||
rows: 12,
|
||||
overflow: 'auto',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
@ -123,6 +132,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
mail_content: modalData.mail_content,
|
||||
cfg: modalData.cfg,
|
||||
extra: modalData.extra,
|
||||
interval: modalData.interval,
|
||||
});
|
||||
value.value = JSON.parse(modalData.cfg || '{}');
|
||||
console.log('value', value.value);
|
||||
@ -132,6 +142,18 @@ const [Modal, modalApi] = useVbenModal({
|
||||
const mMdata = modalApi.getData();
|
||||
const modalData = mMdata.cfg || {};
|
||||
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 = {
|
||||
AppId: mMdata.AppId,
|
||||
Cfg: {
|
||||
@ -143,8 +165,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
level: level,
|
||||
mail_title: values.mail_title,
|
||||
mail_content: values.mail_content,
|
||||
cfg: JSON.stringify(value.value || {}),
|
||||
cfg: cfgStr,
|
||||
extra: values.extra,
|
||||
interval: interval,
|
||||
},
|
||||
};
|
||||
const response = await editActivityApi(params);
|
||||
@ -157,7 +180,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Modal :width="800" title="活动详情">
|
||||
<Modal :width="1200" title="活动详情">
|
||||
<Form>
|
||||
<template #cfg>
|
||||
<JsonEitorVue v-model="value" v-bind="{/* local props & attrs */ }" class="w-[100%] h-[500px]" />
|
||||
@ -165,3 +188,4 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</Form>
|
||||
</Modal>
|
||||
</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 AddActivityModal from './activity-add.vue';
|
||||
import DetailActivityModal from './activity-detail.vue';
|
||||
import SyncActivityModal from './activity-sync.vue';
|
||||
import { activityTypeData } from '#/store/order';
|
||||
import { parseNumber } from '#/store/util';
|
||||
import { $t } from '#/locales'
|
||||
@ -65,8 +66,9 @@ const gridOptions: VxeGridProps<ActivityData> = {
|
||||
{ field: 'id', title: 'id' },
|
||||
{ field: 'type', title: '活动类型', formatter: ({ cellValue }) => activityTypeData[cellValue] || cellValue },
|
||||
{ field: 'level', title: '开启等级', },
|
||||
{ field: 'start_time', title: '开启时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString(), },
|
||||
{ field: 'end_time', title: '结束时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString(), },
|
||||
{ field: 'now_start_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' } },
|
||||
],
|
||||
height: 'auto',
|
||||
@ -95,9 +97,27 @@ const gridOptions: VxeGridProps<ActivityData> = {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
activityList.value = [];
|
||||
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 = '已结束';
|
||||
} else if (item.start_time > now) {
|
||||
} else if (item.now_start_time > now) {
|
||||
item.tag = '未开始';
|
||||
} else {
|
||||
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({
|
||||
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 () => {
|
||||
try {
|
||||
@ -199,12 +233,19 @@ async function addActivity() {
|
||||
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>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height class="h-[800px]">
|
||||
<AddActivityM class="w-[50%]" />
|
||||
<DetailActivityM class="w-[50%]" />
|
||||
<SyncActivityM class="w-[50%]" />
|
||||
<Card class="mb-5" title="活动操作">
|
||||
<div class="mb-5">
|
||||
<Space>
|
||||
@ -215,9 +256,11 @@ async function addActivity() {
|
||||
</div>
|
||||
|
||||
<Space>
|
||||
<Button @click="addActivity">新增活动</Button>
|
||||
<Button type="primary" @click="addActivity">新增活动</Button>
|
||||
</Space>
|
||||
<Space class="ml-5">
|
||||
<Button type="primary" @click="syncCfg">同步活动</Button>
|
||||
</Space>
|
||||
|
||||
</Card>
|
||||
<Grid>
|
||||
<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_after', 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' },
|
||||
],
|
||||
stripe: true,
|
||||
|
||||
@ -316,6 +316,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
info.value.Diamond = r.Diamond;
|
||||
info.value.Energy = r.Energy;
|
||||
info.value.Mac = r.Mac;
|
||||
r.TodayCumulative = r.TodayCumulative || 0;
|
||||
info.value.Cumulative = (r.Cumulative / 3600).toFixed(2) + 'h';
|
||||
info.value.TodayCumulative =
|
||||
(r.TodayCumulative / 3600).toFixed(2) + 'h';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user