版本更新
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
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
@ -4,6 +4,12 @@ import type { UserInfo,AdminLog } from '#/model/admin.user';
|
||||
export interface UserParam {
|
||||
uid: string;
|
||||
}
|
||||
export interface AdminLogParam {
|
||||
username?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
opration?: string;
|
||||
}
|
||||
export async function getAdminInfoApi(param: UserParam) {
|
||||
return requestClient.post<UserInfo>('/admin/info', param);
|
||||
}
|
||||
@ -12,8 +18,8 @@ export async function getAdminListApi() {
|
||||
return requestClient.post<UserInfo[]>('/admin/list');
|
||||
}
|
||||
|
||||
export async function getAdminLogListApi() {
|
||||
return requestClient.post<AdminLog[]>('/admin/log/list');
|
||||
export async function getAdminLogListApi(param:AdminLogParam) {
|
||||
return requestClient.post<AdminLog[]>('/admin/log/list', param);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -12,6 +12,9 @@ export interface MailData {
|
||||
title_en: string;
|
||||
subtitle_en?: string;
|
||||
content_en: string;
|
||||
title_ptbr?: string;
|
||||
subTitle_ptbr?: string;
|
||||
content_ptbr?: string;
|
||||
items : string;
|
||||
start_time: number;
|
||||
end_time: number;
|
||||
|
||||
8
apps/web-antd/src/api/core/operation.ts
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
import type { copyUserParam } from '#/model/type';
|
||||
|
||||
|
||||
export async function copyUser(data:copyUserParam) {
|
||||
return requestClient.post('/operation/copyUser', data);
|
||||
}
|
||||
@ -47,4 +47,8 @@ export async function addLanguageList(data: languageType[]) {
|
||||
|
||||
export async function exportLanguageFile() {
|
||||
return requestClient.post('/language/export', {});
|
||||
}
|
||||
|
||||
export async function deleteLanguageItem(data: {key: string}) {
|
||||
return requestClient.post('/language/delete', data);
|
||||
}
|
||||
@ -39,7 +39,7 @@ const orderTypeMeta: Record<number, { name: string; desc: string }> = {
|
||||
1: { name: '普通订单', desc: '自动生成的基础订单。' },
|
||||
2: { name: '额外订单(弃用)', desc: '历史类型,当前已弃用。' },
|
||||
3: { name: '超级订单', desc: '奖励更高,难度更大的订单类型。' },
|
||||
4: { name: '预热订单', desc: '用于活动或阶段开始前的预热内容。' },
|
||||
4: { name: '预热订单', desc: '新获得发射器生成的特殊订单,订单内容和奖励固定' },
|
||||
5: { name: '触发订单', desc: '由特定事件或条件触发生成。' },
|
||||
6: { name: '退役发射器清理订单', desc: '用于清理退役发射器相关棋子。' },
|
||||
7: { name: '清理无法生成订单的棋子', desc: '处理异常或失效的棋子。' },
|
||||
@ -93,6 +93,8 @@ defineEmits(['click']);
|
||||
<li><b>简单:</b>订单总消耗体力在15-150之间</li>
|
||||
<li><b>中等:</b>订单总消耗体力在100-600之间</li>
|
||||
<li><b>困难:</b>订单总消耗体力在500-1200之间</li>
|
||||
<li><b>零件订单:</b>订单总消耗体力在40-400之间</li>
|
||||
<li><b>消耗品订单:</b>订单总消耗体力在40-200之间</li>
|
||||
</ul>
|
||||
</div>
|
||||
</VbenPopover>
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
"searchField": "Search Field",
|
||||
"value": "Value",
|
||||
"startTime": "Start Time",
|
||||
"endTime": "End Time"
|
||||
"endTime": "End Time",
|
||||
"action": "Action"
|
||||
},
|
||||
"auth": {
|
||||
"login": "Login",
|
||||
@ -45,7 +46,9 @@
|
||||
"languageList": "Language List",
|
||||
"translationList": "Translation List",
|
||||
"lastUpdate": "Last update",
|
||||
"newLineContent": "New Translation"
|
||||
"newLineContent": "New Translation",
|
||||
"noChangesToSave": "No changes to save",
|
||||
"deleteSuccess": "Delete Success"
|
||||
},
|
||||
"operation": {
|
||||
"title": "Operation",
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
"searchField": "搜索列",
|
||||
"value": "值",
|
||||
"startTime": "开始时间",
|
||||
"endTime": "结束时间"
|
||||
"endTime": "结束时间",
|
||||
"action": "操作"
|
||||
},
|
||||
"auth": {
|
||||
"login": "登录",
|
||||
@ -45,14 +46,17 @@
|
||||
"languageList": "语言列表",
|
||||
"translationList": "翻译列表",
|
||||
"lastUpdate": "最后修改日期",
|
||||
"newLineContent": "新增翻译"
|
||||
"newLineContent": "新增翻译",
|
||||
"noChangesToSave": "没有需要保存的更改",
|
||||
"deleteSuccess": "删除成功"
|
||||
},
|
||||
"operation": {
|
||||
"title": "运营管理",
|
||||
"level": "等级分布",
|
||||
"mail": "邮件管理",
|
||||
"order": "订单管理",
|
||||
"language": "翻译管理"
|
||||
"language": "翻译管理",
|
||||
"copyUser": "用户数据复制"
|
||||
},
|
||||
"log": {
|
||||
"event": {
|
||||
@ -88,7 +92,17 @@
|
||||
"emoji_income": "获得表情包",
|
||||
"avatarIcon_income": "获得头像框",
|
||||
"gm": "GM操作",
|
||||
"nickname_set": "设置昵称"
|
||||
"nickname_set": "设置昵称",
|
||||
"logout": "登出",
|
||||
"pet_item_get": "获得宠物物品",
|
||||
"ReqGetChessFromBuff": "从暂存区取棋子",
|
||||
"room_deco_get": "获得房间装饰",
|
||||
"playroom_interact": "playroom互动",
|
||||
"pet_item_use": "使用宠物物品",
|
||||
"room_daily_task":"playroom日常任务",
|
||||
"finish_mini_game":"完成小游戏",
|
||||
"playerdeco_set":"设置玩家装饰",
|
||||
"ReqGetMonthLoginReward":"获取月度登录奖励"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ export interface UserInfo {
|
||||
uid?: string;
|
||||
group: string;
|
||||
role: number;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface AdminLog{
|
||||
|
||||
@ -49,9 +49,9 @@ export interface Chess{
|
||||
export interface languageType {
|
||||
Id: number;
|
||||
key: string;
|
||||
English: string;
|
||||
ChineseSimplified: string;
|
||||
Portuguese: string;
|
||||
en_US: string;
|
||||
zh_CN: string;
|
||||
pt_BR: string;
|
||||
}
|
||||
|
||||
|
||||
@ -64,3 +64,10 @@ export interface languageRecord{
|
||||
Field: string;
|
||||
Update?: number;
|
||||
}
|
||||
|
||||
export interface copyUserParam{
|
||||
src_app: number;
|
||||
src_uid: number;
|
||||
dst_app: number;
|
||||
dst_uid: number;
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
icon: 'lucide:file-clock',
|
||||
icon: 'solar:book-2-bold',
|
||||
order: 1001,
|
||||
title: $t('page.language.title'),
|
||||
},
|
||||
|
||||
@ -36,6 +36,16 @@ const routes: RouteRecordRaw[] = [
|
||||
title: $t('page.operation.mail'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'CopyUser',
|
||||
path: '/copyUser',
|
||||
component: () => import('#/views/operation/copyUser/index.vue'),
|
||||
meta: {
|
||||
affixTab: true,
|
||||
icon: 'lucide:mail',
|
||||
title: $t('page.operation.copyUser'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Order',
|
||||
path: '/order',
|
||||
|
||||
@ -4,25 +4,61 @@ import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { AdminLog } from '#/model/admin.user';
|
||||
import { getAdminLogListApi } from '#/api/core/admin.user';
|
||||
|
||||
import type { AdminLogParam } from '#/api/core/admin.user';
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
const formOptions: VbenFormProps = {
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
filterOption: true,
|
||||
options: [
|
||||
{ label: 'key', value: 'key' },
|
||||
{ label: 'English', value: 'English' },
|
||||
{ label: 'ChineseSimplified', value: 'ChineseSimplified' },
|
||||
{ label: 'Portuguese', value: 'Portuguese' },
|
||||
],
|
||||
placeholder: 'key',
|
||||
showSearch: true,
|
||||
},
|
||||
fieldName: 'SearchField',
|
||||
label: '操作:',
|
||||
},
|
||||
],
|
||||
};
|
||||
const gridOptions: VxeGridProps<AdminLog> = {
|
||||
columns: [
|
||||
{ field: 'admin', title: '用户名', },
|
||||
{ field: 'action', title: '操作' },
|
||||
{ field: 'params', title: '参数' },
|
||||
{ field: 'ip', title: 'IP' },
|
||||
{ field: 'createTime', title: '时间' },
|
||||
{
|
||||
field: 'createTime', title: '时间', formatter: ({ cellValue }) => new Date(cellValue * 1000).toLocaleString()
|
||||
},
|
||||
],
|
||||
height: 'auto',
|
||||
pagerConfig: {},
|
||||
pagerConfig: {
|
||||
pageSize: 100,
|
||||
},
|
||||
proxyConfig: {
|
||||
response: {
|
||||
total: 'total',
|
||||
result: 'data',
|
||||
},
|
||||
ajax: {
|
||||
query: async () => {
|
||||
return await getAdminLogListApi();
|
||||
query: async ({ page }) => {
|
||||
const req: AdminLogParam = {
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
};
|
||||
return await getAdminLogListApi(req);
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -32,7 +68,7 @@ const gridOptions: VxeGridProps<AdminLog> = {
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid] = useVbenVxeGrid({ gridOptions });
|
||||
const [Grid] = useVbenVxeGrid({ formOptions, gridOptions });
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@ -73,6 +73,14 @@ const [Form, FormApi] = useVbenForm({
|
||||
label: '用户组',
|
||||
formItemClass: 'col-span-2',
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
componentProps: {},
|
||||
rules: 'required',
|
||||
fieldName: 'remark',
|
||||
label: '备注',
|
||||
formItemClass: 'col-span-2',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||
@ -90,6 +98,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
phone: values.phone,
|
||||
role: values.role,
|
||||
group: values.group,
|
||||
remark: values.remark,
|
||||
};
|
||||
await addAdminApi(Parms);
|
||||
modalApi.close();
|
||||
|
||||
@ -11,9 +11,10 @@ import addUserModal from './addUser.vue';
|
||||
const gridOptions: VxeGridProps<UserInfo> = {
|
||||
columns: [
|
||||
{ field: 'username', title: '用户名', },
|
||||
{ field: 'phone', title: '手机号' },
|
||||
// { field: 'phone', title: '手机号' },
|
||||
{ field: 'role', title: '角色' },
|
||||
{ field: 'group', title: '用户组' },
|
||||
{ field: 'remark', title: '备注' },
|
||||
],
|
||||
height: 'auto',
|
||||
pagerConfig: {},
|
||||
@ -48,7 +49,7 @@ const addAdmin = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<Page auto-content-height class="h-[1200px]">
|
||||
<addUserM class="w-[50%]" />
|
||||
<Card class="mb-5" title="用户操作">
|
||||
<Space>
|
||||
|
||||
@ -14,13 +14,13 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
columns: [
|
||||
{ title: 'Id', field: 'Id', width: 50 },
|
||||
{ editRender: { name: 'input' }, field: 'key', title: 'key' },
|
||||
{ editRender: { name: 'input' }, field: 'English', title: 'English' },
|
||||
{ editRender: { name: 'input' }, field: 'en_US', title: 'en_US' },
|
||||
{
|
||||
editRender: { name: 'input' },
|
||||
field: 'ChineseSimplified',
|
||||
title: 'ChineseSimplified',
|
||||
field: 'zh_CN',
|
||||
title: 'zh_CN',
|
||||
},
|
||||
{ editRender: { name: 'input' }, field: 'Portuguese', title: 'Portuguese' },
|
||||
{ editRender: { name: 'input' }, field: 'pt_BR', title: 'pt_BR' },
|
||||
],
|
||||
data: ld,
|
||||
pagerConfig: {
|
||||
@ -42,12 +42,9 @@ const [Form] = useVbenForm({
|
||||
show: false,
|
||||
},
|
||||
handleSubmit: async (formValues) => {
|
||||
console.log('Submitting form with values:', formValues);
|
||||
console.log('Language data to add:', ld);
|
||||
// 调用添加语言接口
|
||||
try {
|
||||
await addLanguageList(ld);
|
||||
modalApi.close();
|
||||
} catch (error) {
|
||||
console.error('Error adding languages:', error);
|
||||
}
|
||||
@ -60,7 +57,7 @@ const [Form] = useVbenForm({
|
||||
label: $t('page.language.newLineContent'),
|
||||
componentProps: {
|
||||
disabled: false,
|
||||
placeholder: 'key | English | ChineseSimplified| Portuguese,One record per line',
|
||||
placeholder: 'key | en_US | zh_CN| pt_BR record per line',
|
||||
type: 'textarea',
|
||||
rows: 8,
|
||||
onChange: (e: Event) => {
|
||||
@ -73,9 +70,9 @@ const [Form] = useVbenForm({
|
||||
return {
|
||||
Id: (ld.length + index + 1),
|
||||
key: parts[0] || '',
|
||||
English: parts[1] || '',
|
||||
ChineseSimplified: parts[2] || '',
|
||||
Portuguese: parts[3] || '',
|
||||
en_US: parts[1] || '',
|
||||
zh_CN: parts[2] || '',
|
||||
pt_BR: parts[3] || '',
|
||||
};
|
||||
});
|
||||
ld = ld.concat(newData);
|
||||
@ -91,6 +88,14 @@ const [Modal, modalApi] = useVbenModal({
|
||||
confirmText: '提交',
|
||||
showConfirmButton: false,
|
||||
fullscreen: true,
|
||||
onOpenChange: (open) => {
|
||||
if (!open) {
|
||||
ld = [] as languageType[];
|
||||
GridApi.setGridOptions({
|
||||
data: [],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridProps, VxeGridListeners } from '#/adapter/vxe-table';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Button, notification } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { getLanguageList, exportLanguageFile, saveLanguageList } from '#/api/core/statistics';
|
||||
import { getLanguageList, exportLanguageFile, saveLanguageList, deleteLanguageItem } from '#/api/core/statistics';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import addLanguage from './addLanguage.vue';
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
@ -11,6 +11,9 @@ import type { languageParam } from '#/api/core/statistics';
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { $t } from '#/locales'
|
||||
import dayjs from 'dayjs';
|
||||
import { AccessControl } from '@vben/access';
|
||||
import { useAccess } from '@vben/access';
|
||||
const { hasAccessByRoles } = useAccess();
|
||||
const [AddLanguageModal, AddLanguageModalApi] = useVbenModal({
|
||||
connectedComponent: addLanguage,
|
||||
});
|
||||
@ -19,12 +22,18 @@ let newData: languageType[] = [];
|
||||
let op: languageRecord[] = [];
|
||||
let lastOp: languageRecord[] = [];
|
||||
let lastUpdate: string = '';
|
||||
let columnStr: string = "";
|
||||
let keyVisible: boolean = true;
|
||||
let en_USVisible: boolean = true;
|
||||
let chineseVisible: boolean = true;
|
||||
let pt_BRVisible: boolean = true;
|
||||
let actionVisible: boolean = true;
|
||||
let total: languageType = {
|
||||
Id: 1,
|
||||
key: '',
|
||||
English: '',
|
||||
ChineseSimplified: '',
|
||||
Portuguese: '',
|
||||
en_US: '',
|
||||
zh_CN: '',
|
||||
pt_BR: '',
|
||||
};
|
||||
const startDate = dayjs().subtract(7, 'day').startOf('day');
|
||||
const endDate = dayjs().endOf('day');
|
||||
@ -38,9 +47,9 @@ const formOptions: VbenFormProps = {
|
||||
filterOption: true,
|
||||
options: [
|
||||
{ label: 'key', value: 'key' },
|
||||
{ label: 'English', value: 'English' },
|
||||
{ label: 'ChineseSimplified', value: 'ChineseSimplified' },
|
||||
{ label: 'Portuguese', value: 'Portuguese' },
|
||||
{ label: 'en_US', value: 'en_US' },
|
||||
{ label: 'zh_CN', value: 'zh_CN' },
|
||||
{ label: 'pt_BR', value: 'pt_BR' },
|
||||
],
|
||||
placeholder: 'key',
|
||||
showSearch: true,
|
||||
@ -96,14 +105,15 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
border: true,
|
||||
columns: [
|
||||
{ title: 'Id', field: 'Id', width: 100 },
|
||||
{ editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" } },
|
||||
{ editRender: { name: 'input' }, field: 'English', title: 'English', filters: [{ data: "" }], filterRender: { name: "input" } },
|
||||
{ editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" }, visible: keyVisible },
|
||||
{ editRender: { name: 'input' }, field: 'en_US', title: 'en_US', filters: [{ data: "" }], filterRender: { name: "input" }, visible: en_USVisible },
|
||||
{
|
||||
editRender: { name: 'input' },
|
||||
field: 'ChineseSimplified',
|
||||
title: 'ChineseSimplified', filters: [{ data: "" }], filterRender: { name: "input" }
|
||||
field: 'zh_CN',
|
||||
title: 'zh_CN', filters: [{ data: "" }], filterRender: { name: "input" }, visible: chineseVisible
|
||||
},
|
||||
{ editRender: { name: 'input' }, field: 'Portuguese', title: 'Portuguese', filters: [{ data: "" }], filterRender: { name: "input" } }
|
||||
{ editRender: { name: 'input' }, field: 'pt_BR', title: 'pt_BR', filters: [{ data: "" }], filterRender: { name: "input" }, visible: pt_BRVisible },
|
||||
{ slots: { default: 'action' }, title: $t('page.common.action'), width: 150 },
|
||||
],
|
||||
scrollY: {
|
||||
enabled: true,
|
||||
@ -155,6 +165,15 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
SearchValue: formValues.SearchValue,
|
||||
} as languageParam);
|
||||
newData = response.data || [];
|
||||
columnStr = response.column || "";
|
||||
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');
|
||||
|
||||
}
|
||||
oldData = newData.map(item => ({ ...item }));
|
||||
console.log('API response:', response);
|
||||
lastOp = response.op || [];
|
||||
@ -175,18 +194,18 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
} else {
|
||||
lastUpdate = '';
|
||||
}
|
||||
// 统计 response.data 中 English 字段包含的单词数量(单词由字母、连字符或撇号组成,如 don't、mother-in-law)
|
||||
const englishCount = (response.data || []).reduce((sum: number, item: languageType) => {
|
||||
const s = item?.English || '';
|
||||
// 统计 response.data 中 en_US 字段包含的单词数量(单词由字母、连字符或撇号组成,如 don't、mother-in-law)
|
||||
const en_USCount = (response.data || []).reduce((sum: number, item: languageType) => {
|
||||
const s = item?.en_US || '';
|
||||
const matches = s.match(/[A-Za-z]+(?:['-][A-Za-z]+)*/g);
|
||||
return sum + (matches ? matches.length : 0);
|
||||
}, 0);
|
||||
// 更新 total 变量和 footerData 显示
|
||||
total.English = String(englishCount);
|
||||
total.en_US = String(en_USCount);
|
||||
if (gridOptions && Array.isArray(gridOptions.footerData) && gridOptions.footerData.length > 0) {
|
||||
(gridOptions.footerData[0] as any).English = String(englishCount);
|
||||
(gridOptions.footerData[0] as any).en_US = String(en_USCount);
|
||||
}
|
||||
// 统计 ChineseSimplified 中中文字符总数(只统计汉字)
|
||||
// 统计 zh_CN 中中文字符总数(只统计汉字)
|
||||
const countHan = (str: string) => {
|
||||
try {
|
||||
const m = str.match(/\p{Script=Han}/gu);
|
||||
@ -197,15 +216,41 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
}
|
||||
};
|
||||
|
||||
// 统计 pt_BR 空单元格数量
|
||||
const emptyPtBRCount = (response.data || []).reduce((sum: number, item: languageType) => {
|
||||
const s = item?.pt_BR || '';
|
||||
return sum + (s.trim() === '' ? 1 : 0);
|
||||
}, 0);
|
||||
const emptyZhCnCount = (response.data || []).reduce((sum: number, item: languageType) => {
|
||||
const s = item?.zh_CN || '';
|
||||
return sum + (s.trim() === '' ? 1 : 0);
|
||||
}, 0);
|
||||
const chineseCount = (response.data || []).reduce((sum: number, item: languageType) => {
|
||||
return sum + countHan(item?.ChineseSimplified || '');
|
||||
return sum + countHan(item?.zh_CN || '');
|
||||
}, 0);
|
||||
|
||||
total.ChineseSimplified = String(chineseCount);
|
||||
total.zh_CN = String(chineseCount);
|
||||
if (gridOptions && Array.isArray(gridOptions.footerData) && gridOptions.footerData.length > 0) {
|
||||
(gridOptions.footerData[0] as any).ChineseSimplified = String(chineseCount);
|
||||
(gridOptions.footerData[0] as any).zh_CN = String(chineseCount);
|
||||
}
|
||||
if (hasAccessByRoles(['super', 'admin'])) {
|
||||
actionVisible = true;
|
||||
} else {
|
||||
actionVisible = false;
|
||||
}
|
||||
let length = response.data ? response.data.length : 0;
|
||||
GridApi.setGridOptions({
|
||||
columns: [
|
||||
{ editRender: { name: 'input' }, field: 'key', title: 'key', filters: [{ data: "" }], filterRender: { name: "input" }, visible: keyVisible },
|
||||
{ editRender: { name: 'input' }, field: 'en_US', title: 'en_US', filters: [{ data: "" }], filterRender: { name: "input" }, visible: en_USVisible },
|
||||
{
|
||||
editRender: { name: 'input' },
|
||||
field: 'zh_CN',
|
||||
title: 'zh_CN', filters: [{ data: "" }], filterRender: { name: "input" }, visible: chineseVisible
|
||||
},
|
||||
{ editRender: { name: 'input' }, field: 'pt_BR', title: 'pt_BR', filters: [{ data: "" }], filterRender: { name: "input" }, visible: pt_BRVisible },
|
||||
{ slots: { default: 'action' }, title: $t('page.common.action'), width: 150, visible: actionVisible },
|
||||
],
|
||||
pagerConfig: {
|
||||
enabled: true,
|
||||
pageSize: page.pageSize,
|
||||
@ -214,9 +259,9 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
footerData: [{
|
||||
Id: 'Word',
|
||||
key: '-',
|
||||
English: total.English,
|
||||
ChineseSimplified: total.ChineseSimplified,
|
||||
Portuguese: '',
|
||||
en_US: total.en_US,
|
||||
zh_CN: emptyZhCnCount + '/' + length + ' empty cells',
|
||||
pt_BR: emptyPtBRCount + '/' + length + ' empty cells',
|
||||
}],
|
||||
});
|
||||
return {
|
||||
@ -235,9 +280,15 @@ const gridOptions: VxeGridProps<languageType> = {
|
||||
footerData: [{
|
||||
Id: '字符数统计',
|
||||
key: '-',
|
||||
English: total.English,
|
||||
ChineseSimplified: '',
|
||||
Portuguese: '',
|
||||
en_US: total.en_US,
|
||||
zh_CN: '',
|
||||
pt_BR: '',
|
||||
}, {
|
||||
Id: '空格数统计',
|
||||
key: 'empty',
|
||||
en_US: '',
|
||||
zh_CN: '',
|
||||
pt_BR: '',
|
||||
}],
|
||||
showFooter: true,
|
||||
editConfig: {
|
||||
@ -292,6 +343,13 @@ function addRow() {
|
||||
});
|
||||
}
|
||||
function saveAll() {
|
||||
if (op.length === 0) {
|
||||
notification.info({
|
||||
duration: 10,
|
||||
message: $t('page.language.noChangesToSave'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
saveLanguageList(op).then((response) => {
|
||||
console.log('Save response:', response);
|
||||
op = [];
|
||||
@ -299,33 +357,40 @@ function saveAll() {
|
||||
});
|
||||
}
|
||||
|
||||
function downloadCSV(data: languageType[]) {
|
||||
const headers = ['Id', 'key', 'English', 'ChineseSimplified'];
|
||||
const csvRows = [];
|
||||
csvRows.push(headers.join(','));
|
||||
for (const row of data) {
|
||||
const values = headers.map(header => {
|
||||
const escaped = (row as any)[header]?.toString().replace(/"/g, '""') || '';
|
||||
return `"${escaped}"`;
|
||||
});
|
||||
csvRows.push(values.join(','));
|
||||
}
|
||||
const csvContent = csvRows.join('\n');
|
||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', 'language_export.csv');
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
function exportLang() {
|
||||
exportLanguageFile().then((response) => {
|
||||
// downloadCSV(response.data);
|
||||
const code = response.code || 0;
|
||||
const msg = response.msg || '';
|
||||
if (code !== 0) {
|
||||
notification.error({
|
||||
duration: 10,
|
||||
message: msg,
|
||||
});
|
||||
return;
|
||||
}
|
||||
notification.success({
|
||||
duration: 10,
|
||||
message: $t('page.language.exportSuccess'),
|
||||
});
|
||||
});
|
||||
}
|
||||
function deleteRow(row: languageType) {
|
||||
deleteLanguageItem({ key: row.key }).then((response) => {
|
||||
const code = response.code || 0;
|
||||
const msg = response.msg || '';
|
||||
if (code !== 0) {
|
||||
notification.error({
|
||||
duration: 10,
|
||||
message: msg,
|
||||
});
|
||||
return;
|
||||
}
|
||||
notification.success({
|
||||
duration: 10,
|
||||
message: $t('page.language.deleteSuccess'),
|
||||
});
|
||||
GridApi.reload();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@ -340,8 +405,8 @@ function exportLang() {
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="h-full flex flex-col">
|
||||
<Page auto-content-height class="h-[800px]">
|
||||
<div class="min-h-screen flex flex-col">
|
||||
<Page auto-content-height>
|
||||
<AddLanguageModal width="1200px" height="1200px"></AddLanguageModal />
|
||||
<Grid>
|
||||
<template #toolbar-tools>
|
||||
@ -352,6 +417,12 @@ function exportLang() {
|
||||
<Button type="primary" @click="saveAll" class="mr-2"> {{ $t('page.common.save') }} </Button>
|
||||
<Button type="primary" @click="exportLang"> {{ $t('page.common.gitCommit') }} </Button>
|
||||
</template>
|
||||
|
||||
<template #action="{ row }">
|
||||
<AccessControl :codes="['super', 'admin']" type="role">
|
||||
<Button type="primary" @click="deleteRow(row)">删除</Button>
|
||||
</AccessControl>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</div>
|
||||
|
||||
108
apps/web-antd/src/views/operation/copyUser/copy.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<script setup lang="ts">
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { message } from 'ant-design-vue'
|
||||
import { copyUser } from '#/api/core/operation';
|
||||
import type { copyUserParam } from '#/model/type';
|
||||
const [Form] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full h-full',
|
||||
},
|
||||
},
|
||||
// 使用 tailwindcss grid布局
|
||||
// 提交函数
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
layout: 'horizontal',
|
||||
// 水平布局,label和input在同一行
|
||||
schema: [
|
||||
{
|
||||
component: 'Select',
|
||||
fieldName: 'src_app',
|
||||
label: '源应用ID',
|
||||
rules: 'required',
|
||||
defaultValue: 1,
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'QA环境',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '测试环境',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: 'US环境',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'src_uid',
|
||||
label: '源用户ID',
|
||||
rules: 'required',
|
||||
defaultValue: 100100013,
|
||||
componentProps: {
|
||||
placeholder: '请输入源用户ID',
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
fieldName: 'dst_app',
|
||||
label: '目标应用ID',
|
||||
defaultValue: 1,
|
||||
rules: 'required',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'QA环境',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '测试环境',
|
||||
value: 1,
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'dst_uid',
|
||||
label: '目标用户ID',
|
||||
defaultValue: 100100014,
|
||||
rules: 'required',
|
||||
componentProps: {
|
||||
placeholder: '请输入目标用户ID',
|
||||
},
|
||||
},
|
||||
],
|
||||
handleSubmit: onSubmit,
|
||||
|
||||
});
|
||||
|
||||
async function onSubmit(values: Record<string, any>) {
|
||||
const Param: copyUserParam = {
|
||||
src_app: Number(values.src_app),
|
||||
src_uid: Number(values.src_uid),
|
||||
dst_app: Number(values.dst_app),
|
||||
dst_uid: Number(values.dst_uid),
|
||||
};
|
||||
await copyUser(Param).then((respone) => {
|
||||
if (respone.code === 0) {
|
||||
message.success('用户复制成功');
|
||||
} else {
|
||||
message.error(`用户复制失败: ${respone.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<Form class="mt-20" />
|
||||
|
||||
</template>
|
||||
11
apps/web-antd/src/views/operation/copyUser/index.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
|
||||
import AnalyticsVisitsTable from './copy.vue';
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<AnalyticsVisitsTable />
|
||||
</template>
|
||||
@ -61,6 +61,27 @@ const [Form, FormApi] = useVbenForm({
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'TitlePTBR',
|
||||
label: '葡萄牙(巴西)邮件标题',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'SubtitlePTBR',
|
||||
label: '葡萄牙(巴西)邮件副标题',
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
fieldName: 'ContentPTBR',
|
||||
label: '葡萄牙(巴西)邮件内容',
|
||||
componentProps: {
|
||||
type: 'textarea',
|
||||
rows: 8,
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
fieldName: 'Items',
|
||||
@ -182,6 +203,9 @@ const [Modal, modalApi] = useVbenModal({
|
||||
title_en: TitleEN,
|
||||
subtitle_en: SubtitleEN,
|
||||
content_en: ContentEN,
|
||||
title_ptbr: values.TitlePTBR,
|
||||
subTitle_ptbr: values.SubtitlePTBR,
|
||||
content_ptbr: values.ContentPTBR,
|
||||
items: Items,
|
||||
to_uids: ToUids,
|
||||
start_time: start_time,
|
||||
|
||||
@ -76,11 +76,11 @@ const gridOptions: VxeGridProps<MailData> = {
|
||||
columns: [
|
||||
{ field: 'mail_id', title: 'id' },
|
||||
{ field: 'title', title: '邮件标题' },
|
||||
{ field: 'title_en', title: '英文邮件标题' },
|
||||
// { field: 'title_en', title: '英文邮件标题' },
|
||||
{ field: 'subtitle', title: '邮件副标题' },
|
||||
{ field: 'subtitleEN', title: '英文邮件副标题' },
|
||||
// { field: 'subtitleEN', title: '英文邮件副标题' },
|
||||
{ field: 'content', title: '邮件内容' },
|
||||
{ field: 'content_en', title: '英文邮件内容' },
|
||||
// { field: 'content_en', title: '英文邮件内容' },
|
||||
{ field: 'items', title: '道具' },
|
||||
{
|
||||
field: 'mail_type',
|
||||
|
||||
@ -114,6 +114,41 @@ const gridOptions: VxeGridProps<RowType> = {
|
||||
}
|
||||
state.uid = Uid;
|
||||
state.Event = formValues.Event;
|
||||
let startTimeUnix = 0;
|
||||
let endTimeUnix = 0;
|
||||
// formValues.StartTime/EndTime 可能是 string(当 DatePicker 设置了 valueFormat 时)
|
||||
// 也可能是 dayjs 对象(未设置 valueFormat 时)
|
||||
if (formValues.StartTime) {
|
||||
if (typeof formValues.StartTime === 'string') {
|
||||
startTimeUnix = dayjs(formValues.StartTime).unix();
|
||||
} else if (dayjs.isDayjs(formValues.StartTime)) {
|
||||
startTimeUnix = formValues.StartTime.unix();
|
||||
} else if (typeof (formValues.StartTime as any).unix === 'function') {
|
||||
startTimeUnix = (formValues.StartTime as any).unix();
|
||||
} else {
|
||||
startTimeUnix = dayjs(formValues.StartTime).unix();
|
||||
}
|
||||
}
|
||||
if (formValues.EndTime) {
|
||||
if (typeof formValues.EndTime === 'string') {
|
||||
endTimeUnix = dayjs(formValues.EndTime).unix();
|
||||
} else if (dayjs.isDayjs(formValues.EndTime)) {
|
||||
endTimeUnix = formValues.EndTime.unix();
|
||||
} else if (typeof (formValues.EndTime as any).unix === 'function') {
|
||||
endTimeUnix = (formValues.EndTime as any).unix();
|
||||
} else {
|
||||
endTimeUnix = dayjs(formValues.EndTime).unix();
|
||||
}
|
||||
}
|
||||
// 使用计算好的 unix 时间调用接口
|
||||
return await getUserlogEventApi({
|
||||
Id: Uid,
|
||||
Event: formValues.Event,
|
||||
StartTime: startTimeUnix,
|
||||
EndTime: endTimeUnix,
|
||||
CurrentPage: page.currentPage,
|
||||
PageSize: page.pageSize,
|
||||
});
|
||||
return await getUserlogEventApi({
|
||||
Id: Uid,
|
||||
Event: formValues.Event,
|
||||
|
||||
@ -57,10 +57,24 @@ const [BaseForm] = useVbenForm({
|
||||
{ label: 'addexp 100 增加100经验', value: 'addexp 100' },
|
||||
{ label: 'setlv 1 设置为1级', value: 'setlv 1' },
|
||||
{ label: 'AllFace 获取所有头像和头像框', value: 'AllFace' },
|
||||
{ label: 'resetFace 重置用户头像和头像框', value: 'resetFace' },
|
||||
{ label: 'addEmoji 获取所有表情', value: 'addEmoji' },
|
||||
{ label: 'initEmoji 重置表情', value: 'initEmoji' },
|
||||
|
||||
|
||||
// 订单
|
||||
{ label: 'reset_order 重置订单', value: 'reset_order' },
|
||||
{ label: 'orderMerge 获取订单所需棋子', value: 'orderMerge' },
|
||||
// 模拟充值
|
||||
{ label: 'pay 10 模拟充值10挡位', value: 'pay 10' },
|
||||
// 棋盘
|
||||
{ label: 'AddPart 增加所有类型零件10000个', value: 'AddPart' },
|
||||
{ label: 'resetChess 重置棋盘', value: 'resetChess' },
|
||||
{ label: 'addChessStar 5 增加5点棋子碎片', value: 'addChessStar 5' },
|
||||
{ label: 'resetRetire 重置棋子退役', value: 'resetRetire' },
|
||||
{ label: 'cleanBuff 清空缓存区', value: 'cleanBuff' },
|
||||
{ label: 'addAllChess 获取所有棋子', value: 'addAllChess' },
|
||||
{ label: 'addSeasonChess 获取当前轮次全部棋子', value: 'addSeasonChess' },
|
||||
|
||||
// 卡牌
|
||||
{ label: 'add_card_star 5 增加5点卡包碎片', value: 'add_card_star 5' },
|
||||
@ -68,9 +82,11 @@ const [BaseForm] = useVbenForm({
|
||||
{ label: 'addAddCard 获取所有卡牌', value: 'addAddCard' },
|
||||
{ label: 'addAddCard 获取当前轮次全部卡牌', value: 'addAddCard' },
|
||||
{ label: 'resetCardSeasonFirst 重置卡牌赛季初始奖励', value: 'resetCardSeasonFirst' },
|
||||
{ label: 'reset_card_reward 重置卡牌大奖', value: 'reset_card_reward' },
|
||||
|
||||
// 每日任务
|
||||
{ label: 'setSevenLoginActive 20 设置签到进度为20', value: 'setSevenLoginActive 20' },
|
||||
{ label: 'addDailyActive 增加每日任务活跃度', value: 'addDailyActive 10' },
|
||||
|
||||
// 限时事件
|
||||
{ label: 'setProgress 4 设置转盘进度为4', value: 'setProgress 4' },
|
||||
@ -91,16 +107,28 @@ const [BaseForm] = useVbenForm({
|
||||
{ label: 'resetTriggerTime 重置playroom触发订单cd', value: 'resetTriggerTime' },
|
||||
{ label: 'playroomDress 获取playroom所有服装', value: 'playroomDress' },
|
||||
{ label: 'playroomAir 获取playroom所有飞行背包', value: 'playroomAir' },
|
||||
{ label: 'resetCollect 重置playroom家具', value: 'resetCollect' },
|
||||
{ label: 'playroomDress 解锁所有playroom服装', value: 'playroomDress' },
|
||||
{ label: 'resetAir 重置playroom飞行背包', value: 'resetAir' },
|
||||
|
||||
// 好友
|
||||
{ label: 'addFriend 对方uid 仅用于调试,对方好友列表不会增加', value: 'addFriend 对方uid' },
|
||||
|
||||
// 场景
|
||||
{ label: 'setDecorateArea 1 设置装饰区域id', value: 'setDecorateArea 1' },
|
||||
{ label: 'setDecorateProgresse 1 设置装饰进度', value: 'setDecorateProgresse 1' },
|
||||
{ label: 'setDecorateProgress 1 设置装饰进度', value: 'setDecorateProgress 1' },
|
||||
|
||||
// 图鉴
|
||||
{ label: 'handbook 解锁所有图鉴', value: 'handbook' },
|
||||
|
||||
// 猜颜色
|
||||
{ label: 'guessColorReload 重置猜颜色游戏', value: 'guessColorReload' },
|
||||
// 挖矿
|
||||
{ label: 'miningReload 重置挖矿游戏', value: 'miningReload' },
|
||||
// 赛跑
|
||||
{ label: 'raceReload 重置赛跑游戏', value: 'raceReload' },
|
||||
// 猫草大作战
|
||||
{ label: 'catnipReload 重置猫草大作战游戏', value: 'catnipReload' },
|
||||
],
|
||||
},
|
||||
label: 'Gm:',
|
||||
|
||||
@ -36,7 +36,7 @@ const { authPanelCenter, authPanelLeft, authPanelRight, isDark } =
|
||||
<template>
|
||||
<div
|
||||
:class="[isDark]"
|
||||
class="flex min-h-full flex-1 select-none overflow-x-hidden"
|
||||
class="flex min-h-screen flex-1 select-none overflow-x-hidden"
|
||||
>
|
||||
<template v-if="toolbar">
|
||||
<slot name="toolbar">
|
||||
@ -46,7 +46,7 @@ const { authPanelCenter, authPanelLeft, authPanelRight, isDark } =
|
||||
<!-- 左侧认证面板 -->
|
||||
<AuthenticationFormView
|
||||
v-if="authPanelLeft"
|
||||
class="min-h-full w-2/5 flex-1"
|
||||
class="min-h-screen w-2/5 flex-1"
|
||||
transition-name="slide-left"
|
||||
>
|
||||
<template v-if="copyright" #copyright>
|
||||
@ -133,38 +133,22 @@ const { authPanelCenter, authPanelLeft, authPanelRight, isDark } =
|
||||
<style scoped>
|
||||
.login-background {
|
||||
background: linear-gradient(
|
||||
-45deg,
|
||||
#ff0000,
|
||||
#ff4500,
|
||||
#ff7f00,
|
||||
#ffa500,
|
||||
#ffff00,
|
||||
#ffd700,
|
||||
#ff6347,
|
||||
#ff1493,
|
||||
#ff0000
|
||||
154deg,
|
||||
#07070915 30%,
|
||||
hsl(var(--primary) / 30%) 48%,
|
||||
#07070915 64%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: rainbow-flow 8s ease-in-out infinite;
|
||||
filter: blur(100px);
|
||||
}
|
||||
|
||||
.dark {
|
||||
.login-background {
|
||||
background: linear-gradient(
|
||||
-45deg,
|
||||
#ff000080,
|
||||
#ff450080,
|
||||
#ff7f0080,
|
||||
#ffa50080,
|
||||
#ffff0080,
|
||||
#ffd70080,
|
||||
#ff634780,
|
||||
#ff149380,
|
||||
#ff000080
|
||||
154deg,
|
||||
#07070915 30%,
|
||||
hsl(var(--primary) / 20%) 48%,
|
||||
#07070915 64%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: rainbow-flow 8s ease-in-out infinite;
|
||||
filter: blur(100px);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
首次登入游戏 加入了拯救小猫的行列!
|
||||
完成休息室 为小猫建造了一个温暖的家!
|
||||
完成餐厅 为小猫准备了丰盛的食物!
|
||||
完成浴室 把小猫整理得香喷喷的!
|
||||
完成衣帽间 把小猫打扮得漂漂亮亮的!
|
||||
获得新头像 收藏了一个新的头像!
|
||||
获得新头像框 收藏了一个新的头像框!
|
||||
获得新表情 收藏了一个新的表情!
|
||||
获得新装饰品 获得了新的房间装饰!
|
||||
获得新服装 获得了漂漂亮亮的新衣服!
|
||||
完成卡册收集 收集了XXX的所有卡牌!
|
||||
完成全卡牌收集 收集了XXX的所有卡牌!
|
||||
获得锦标赛名次 在锦标赛中获得了第X名!
|
||||
获得锦标赛大奖 完成了锦标赛!
|
||||
获得限时活动大奖 在XXX活动中获得了YYY!
|
||||
参加好友合作类活动 参加了合作伙伴活动!
|
||||
获得拜访小游戏大奖 把邻居家的小猫关了起来!
|
||||
获得拜访小游戏大奖 薅走了邻居的宠物币!
|
||||
打开宠物宝藏 与好友们一起找到了宝藏!
|
||||
拜访时点赞 给邻居点了个大大的赞!
|
||||
完成图鉴收集成就 收集了XXX的所有物品!
|
||||
完成第X章所有场景 将章节X的变得焕然一新!
|
||||
流失用户回归 回来看小猫了!
|
||||