后台管理上限
This commit is contained in:
6
.env
6
.env
@@ -20,9 +20,9 @@ VITE_APP_DOCALERT_ENABLE=true
|
||||
VITE_APP_BAIDU_CODE = a1ff8825baa73c3a78eb96aa40325abc
|
||||
|
||||
# 默认账户密码
|
||||
VITE_APP_DEFAULT_LOGIN_TENANT = 芋道源码
|
||||
VITE_APP_DEFAULT_LOGIN_USERNAME = admin
|
||||
VITE_APP_DEFAULT_LOGIN_PASSWORD = admin123
|
||||
# VITE_APP_DEFAULT_LOGIN_TENANT = keyLove
|
||||
# VITE_APP_DEFAULT_LOGIN_USERNAME = admin
|
||||
# VITE_APP_DEFAULT_LOGIN_PASSWORD = admin123
|
||||
|
||||
# API 加解密
|
||||
VITE_APP_API_ENCRYPT_ENABLE = true
|
||||
|
||||
@@ -5,6 +5,7 @@ VITE_DEV=true
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL='http://192.168.2.22:48081'
|
||||
# VITE_BASE_URL='https://admin-xtm9a.loveamorkey.com'
|
||||
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
||||
VITE_UPLOAD_TYPE=server
|
||||
@@ -28,7 +29,7 @@ VITE_BASE_PATH=/
|
||||
VITE_MALL_H5_DOMAIN='http://localhost:3000'
|
||||
|
||||
# 验证码的开关
|
||||
VITE_APP_CAPTCHA_ENABLE=false
|
||||
VITE_APP_CAPTCHA_ENABLE=true
|
||||
|
||||
# GoView域名
|
||||
VITE_GOVIEW_URL='http://127.0.0.1:3000'
|
||||
@@ -4,8 +4,8 @@ NODE_ENV=production
|
||||
VITE_DEV=false
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL='http://localhost:48080'
|
||||
|
||||
# VITE_BASE_URL='http://localhost:48080'
|
||||
VITE_BASE_URL='https://admin-xtm9a.loveamorkey.com'
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||
VITE_UPLOAD_TYPE=server
|
||||
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
import request from '@/config/axios'
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
/** AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍信息 */
|
||||
export interface AiCompanionI18n {
|
||||
id: number; // 主键ID
|
||||
companionId?: number; // 陪聊角色主表ID,对应 keyboard_ai_companion.id
|
||||
locale?: string; // 语言标识,如 zh-CN、en-US、ja-JP
|
||||
name?: string; // 角色名称(多语言)
|
||||
shortDesc: string; // 一句话人设描述(多语言)
|
||||
introText: string; // 角色详细介绍文案(多语言)
|
||||
createdAt?: string | Dayjs; // 创建时间
|
||||
updatedAt?: string | Dayjs; // 更新时间
|
||||
}
|
||||
|
||||
/** AI陪聊角色表,用于定义恋爱/陪伴型虚拟角色的基础信息与人设信息 */
|
||||
export interface AiCompanion {
|
||||
id: number; // 陪聊角色唯一ID
|
||||
name?: string; // 角色名称(展示用,如:Katie Leona)
|
||||
avatarUrl: string; // 角色头像URL,用于列表页和聊天页
|
||||
coverImageUrl: string; // 角色封面图URL,用于角色详情页
|
||||
gender: string; // 角色性别(male / female / other)
|
||||
ageRange: string; // 角色年龄段描述(如:20s、25-30)
|
||||
shortDesc: string; // 一句话人设描述,用于卡片或列表展示
|
||||
introText: string; // 角色详细介绍文案,用于角色详情页
|
||||
personalityTags: object; // 角色性格标签数组(如:温柔、黏人、治愈)
|
||||
speakingStyle: string; // 角色说话风格(如:撒娇型、理性型、活泼型)
|
||||
systemPrompt?: string; // AI系统Prompt,定义角色核心人设,仅供模型使用
|
||||
@@ -60,5 +69,36 @@ export const AiCompanionApi = {
|
||||
// 导出AI陪聊角色表,用于定义恋爱/陪伴型虚拟角色的基础信息与人设 Excel
|
||||
exportAiCompanion: async (params) => {
|
||||
return await request.download({ url: `/keyboard/ai-companion/export-excel`, params })
|
||||
},
|
||||
|
||||
// ==================== 子表(AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍) ====================
|
||||
|
||||
// 获得AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍分页
|
||||
getAiCompanionI18nPage: async (params) => {
|
||||
return await request.get({ url: `/keyboard/ai-companion/ai-companion-i18n/page`, params })
|
||||
},
|
||||
// 新增AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍
|
||||
createAiCompanionI18n: async (data: AiCompanionI18n) => {
|
||||
return await request.post({ url: `/keyboard/ai-companion/ai-companion-i18n/create`, data })
|
||||
},
|
||||
|
||||
// 修改AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍
|
||||
updateAiCompanionI18n: async (data: AiCompanionI18n) => {
|
||||
return await request.put({ url: `/keyboard/ai-companion/ai-companion-i18n/update`, data })
|
||||
},
|
||||
|
||||
// 删除AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍
|
||||
deleteAiCompanionI18n: async (id: number) => {
|
||||
return await request.delete({ url: `/keyboard/ai-companion/ai-companion-i18n/delete?id=` + id })
|
||||
},
|
||||
|
||||
/** 批量删除AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍 */
|
||||
deleteAiCompanionI18nList: async (ids: number[]) => {
|
||||
return await request.delete({ url: `/keyboard/ai-companion/ai-companion-i18n/delete-list?ids=${ids.join(',')}` })
|
||||
},
|
||||
|
||||
// 获得AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍
|
||||
getAiCompanionI18n: async (id: number) => {
|
||||
return await request.get({ url: `/keyboard/ai-companion/ai-companion-i18n/get?id=` + id })
|
||||
}
|
||||
}
|
||||
61
src/api/keyboard/appversions/index.ts
Normal file
61
src/api/keyboard/appversions/index.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import request from '@/config/axios'
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
/** App 版本发布与更新检查表:区分 Android/iOS、渠道,支持最低支持版本与强更策略。信息 */
|
||||
export interface AppVersions {
|
||||
id: number; // 主键,自增版本记录ID。
|
||||
appId?: string; // 应用标识(支持多App/多包名场景);单App可固定为 main。
|
||||
platform?: string; // 平台:android 或 ios(用 CHECK 约束限制取值)。
|
||||
channel?: string; // 渠道标识:如 official / huawei / xiaomi / testflight 等,用于区分不同分发包。
|
||||
versionName?: string; // 展示用版本号(语义版本字符串),如 1.2.3。
|
||||
versionCode?: number; // 比较用版本号(整数递增):Android 对应 versionCode;iOS 建议维护同样的递增值以便比较。
|
||||
buildNumber: string; // iOS 可选构建号(例如 CFBundleVersion),通常为字符串;用于追溯构建或与CI编号对齐。
|
||||
minSupportedCode?: number; // 最低支持版本号(整数):客户端 version_code 低于该值必须更新/可拒绝继续使用。
|
||||
isForceUpdate?: boolean; // 是否强制更新:当客户端未达到最新版本且此字段为 true,可要求强更(即使 >= min_supported_code)。
|
||||
isActive?: boolean; // 是否生效:true 表示该版本记录可用于对外更新检查;false 用于下架/撤回。
|
||||
releaseNotes: string; // 更新说明(展示给用户的版本更新内容)。
|
||||
downloadUrl: string; // 下载链接:Android 可为 apk 直链/市场 scheme;iOS 通常为 App Store 链接或统一跳转页。
|
||||
storeUrl: string; // 应用市场/商店页面链接(可选,若 download_url 已覆盖可不填)。
|
||||
metadata?: object; // 扩展元数据(JSON):如包大小、md5、签名信息、最低系统版本等。
|
||||
releasedAt?: string | Dayjs; // 发布时间(对外宣布/上线时间),用于展示与排序。
|
||||
createdAt?: string | Dayjs; // 记录创建时间。
|
||||
updatedAt?: string | Dayjs; // 记录更新时间(建议配合触发器自动维护)。
|
||||
}
|
||||
|
||||
// App 版本发布与更新检查表:区分 Android/iOS、渠道,支持最低支持版本与强更策略。 API
|
||||
export const AppVersionsApi = {
|
||||
// 查询App 版本发布与更新检查表:区分 Android/iOS、渠道,支持最低支持版本与强更策略。分页
|
||||
getAppVersionsPage: async (params: any) => {
|
||||
return await request.get({ url: `/keyboard/app-versions/page`, params })
|
||||
},
|
||||
|
||||
// 查询App 版本发布与更新检查表:区分 Android/iOS、渠道,支持最低支持版本与强更策略。详情
|
||||
getAppVersions: async (id: number) => {
|
||||
return await request.get({ url: `/keyboard/app-versions/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增App 版本发布与更新检查表:区分 Android/iOS、渠道,支持最低支持版本与强更策略。
|
||||
createAppVersions: async (data: AppVersions) => {
|
||||
return await request.post({ url: `/keyboard/app-versions/create`, data })
|
||||
},
|
||||
|
||||
// 修改App 版本发布与更新检查表:区分 Android/iOS、渠道,支持最低支持版本与强更策略。
|
||||
updateAppVersions: async (data: AppVersions) => {
|
||||
return await request.put({ url: `/keyboard/app-versions/update`, data })
|
||||
},
|
||||
|
||||
// 删除App 版本发布与更新检查表:区分 Android/iOS、渠道,支持最低支持版本与强更策略。
|
||||
deleteAppVersions: async (id: number) => {
|
||||
return await request.delete({ url: `/keyboard/app-versions/delete?id=` + id })
|
||||
},
|
||||
|
||||
/** 批量删除App 版本发布与更新检查表:区分 Android/iOS、渠道,支持最低支持版本与强更策略。 */
|
||||
deleteAppVersionsList: async (ids: number[]) => {
|
||||
return await request.delete({ url: `/keyboard/app-versions/delete-list?ids=${ids.join(',')}` })
|
||||
},
|
||||
|
||||
// 导出App 版本发布与更新检查表:区分 Android/iOS、渠道,支持最低支持版本与强更策略。 Excel
|
||||
exportAppVersions: async (params) => {
|
||||
return await request.download({ url: `/keyboard/app-versions/export-excel`, params })
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,17 @@
|
||||
import request from '@/config/axios'
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
/** 键盘人设国际化内容信息 */
|
||||
export interface CharacterI18n {
|
||||
id: number; // 主键 Id
|
||||
characterId?: number; // 角色主表id
|
||||
locale?: string; // 语言标识,如 zh-CN/en-US/ja-JP
|
||||
characterName: string; // 标题
|
||||
characterBackground: string; // 背景描述
|
||||
createdAt: string | Dayjs; // 创建时间
|
||||
updatedAt: string | Dayjs; // 更新时间
|
||||
}
|
||||
|
||||
/** 键盘人设信息 */
|
||||
export interface Character {
|
||||
id: number; // 主键 Id
|
||||
@@ -51,5 +62,36 @@ export const CharacterApi = {
|
||||
// 导出键盘人设 Excel
|
||||
exportCharacter: async (params) => {
|
||||
return await request.download({ url: `/keyboard/character/export-excel`, params })
|
||||
},
|
||||
|
||||
// ==================== 子表(键盘人设国际化内容) ====================
|
||||
|
||||
// 获得键盘人设国际化内容分页
|
||||
getCharacterI18nPage: async (params) => {
|
||||
return await request.get({ url: `/keyboard/character/character-i18n/page`, params })
|
||||
},
|
||||
// 新增键盘人设国际化内容
|
||||
createCharacterI18n: async (data: CharacterI18n) => {
|
||||
return await request.post({ url: `/keyboard/character/character-i18n/create`, data })
|
||||
},
|
||||
|
||||
// 修改键盘人设国际化内容
|
||||
updateCharacterI18n: async (data: CharacterI18n) => {
|
||||
return await request.put({ url: `/keyboard/character/character-i18n/update`, data })
|
||||
},
|
||||
|
||||
// 删除键盘人设国际化内容
|
||||
deleteCharacterI18n: async (id: number) => {
|
||||
return await request.delete({ url: `/keyboard/character/character-i18n/delete?id=` + id })
|
||||
},
|
||||
|
||||
/** 批量删除键盘人设国际化内容 */
|
||||
deleteCharacterI18nList: async (ids: number[]) => {
|
||||
return await request.delete({ url: `/keyboard/character/character-i18n/delete-list?ids=${ids.join(',')}` })
|
||||
},
|
||||
|
||||
// 获得键盘人设国际化内容
|
||||
getCharacterI18n: async (id: number) => {
|
||||
return await request.get({ url: `/keyboard/character/character-i18n/get?id=` + id })
|
||||
}
|
||||
}
|
||||
53
src/api/keyboard/characteri18n/index.ts
Normal file
53
src/api/keyboard/characteri18n/index.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import request from '@/config/axios'
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
/** 键盘人设国际化内容信息 */
|
||||
export interface CharacterI18n {
|
||||
id: number; // 主键 Id
|
||||
characterId?: number; // 角色主表id
|
||||
locale?: string; // 语言标识,如 zh-CN/en-US/ja-JP
|
||||
characterName: string; // 标题
|
||||
characterBackground: string; // 背景描述
|
||||
prompt: string; // 人设提示词
|
||||
emoji: string; // emoji 标签
|
||||
createdAt: string | Dayjs; // 创建时间
|
||||
updatedAt: string | Dayjs; // 更新时间
|
||||
}
|
||||
|
||||
// 键盘人设国际化内容 API
|
||||
export const CharacterI18nApi = {
|
||||
// 查询键盘人设国际化内容分页
|
||||
getCharacterI18nPage: async (params: any) => {
|
||||
return await request.get({ url: `/keyboard/character-i18n/page`, params })
|
||||
},
|
||||
|
||||
// 查询键盘人设国际化内容详情
|
||||
getCharacterI18n: async (id: number) => {
|
||||
return await request.get({ url: `/keyboard/character-i18n/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增键盘人设国际化内容
|
||||
createCharacterI18n: async (data: CharacterI18n) => {
|
||||
return await request.post({ url: `/keyboard/character-i18n/create`, data })
|
||||
},
|
||||
|
||||
// 修改键盘人设国际化内容
|
||||
updateCharacterI18n: async (data: CharacterI18n) => {
|
||||
return await request.put({ url: `/keyboard/character-i18n/update`, data })
|
||||
},
|
||||
|
||||
// 删除键盘人设国际化内容
|
||||
deleteCharacterI18n: async (id: number) => {
|
||||
return await request.delete({ url: `/keyboard/character-i18n/delete?id=` + id })
|
||||
},
|
||||
|
||||
/** 批量删除键盘人设国际化内容 */
|
||||
deleteCharacterI18nList: async (ids: number[]) => {
|
||||
return await request.delete({ url: `/keyboard/character-i18n/delete-list?ids=${ids.join(',')}` })
|
||||
},
|
||||
|
||||
// 导出键盘人设国际化内容 Excel
|
||||
exportCharacterI18n: async (params) => {
|
||||
return await request.download({ url: `/keyboard/character-i18n/export-excel`, params })
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,19 @@
|
||||
import request from '@/config/axios'
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
/** 人设标签国际化信息 */
|
||||
export interface TagI18n {
|
||||
id: number; // 主键 Id
|
||||
tagId?: number; // 标签主表ID,对应 keyboard_tag.id
|
||||
locale?: string; // 语言标识,如 zh-CN、en-US、ja-JP
|
||||
tagName?: string; // 标签名称(多语言)
|
||||
createdAt: string | Dayjs; // 创建时间
|
||||
updatedAt: string | Dayjs; // 更新时间
|
||||
}
|
||||
|
||||
/** 人设标签信息 */
|
||||
export interface Tag {
|
||||
id: number; // 主键 Id
|
||||
tagName: string; // 标签名
|
||||
createdAt: string | Dayjs; // 创建时间
|
||||
updatedAt: string | Dayjs; // 更新时间
|
||||
}
|
||||
@@ -44,5 +53,36 @@ export const TagApi = {
|
||||
// 导出人设标签 Excel
|
||||
exportTag: async (params) => {
|
||||
return await request.download({ url: `/keyboard/tag/export-excel`, params })
|
||||
},
|
||||
|
||||
// ==================== 子表(人设标签国际化) ====================
|
||||
|
||||
// 获得人设标签国际化分页
|
||||
getTagI18nPage: async (params) => {
|
||||
return await request.get({ url: `/keyboard/tag/tag-i18n/page`, params })
|
||||
},
|
||||
// 新增人设标签国际化
|
||||
createTagI18n: async (data: TagI18n) => {
|
||||
return await request.post({ url: `/keyboard/tag/tag-i18n/create`, data })
|
||||
},
|
||||
|
||||
// 修改人设标签国际化
|
||||
updateTagI18n: async (data: TagI18n) => {
|
||||
return await request.put({ url: `/keyboard/tag/tag-i18n/update`, data })
|
||||
},
|
||||
|
||||
// 删除人设标签国际化
|
||||
deleteTagI18n: async (id: number) => {
|
||||
return await request.delete({ url: `/keyboard/tag/tag-i18n/delete?id=` + id })
|
||||
},
|
||||
|
||||
/** 批量删除人设标签国际化 */
|
||||
deleteTagI18nList: async (ids: number[]) => {
|
||||
return await request.delete({ url: `/keyboard/tag/tag-i18n/delete-list?ids=${ids.join(',')}` })
|
||||
},
|
||||
|
||||
// 获得人设标签国际化
|
||||
getTagI18n: async (id: number) => {
|
||||
return await request.get({ url: `/keyboard/tag/tag-i18n/get?id=` + id })
|
||||
}
|
||||
}
|
||||
49
src/api/keyboard/warningmessage/index.ts
Normal file
49
src/api/keyboard/warningmessage/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import request from '@/config/axios'
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
/** 用户注销提示信息信息 */
|
||||
export interface WarningMessage {
|
||||
id: number; // 主键
|
||||
locale: string; // 地区
|
||||
content: string; // 正文
|
||||
updatedAt: string | Dayjs; // 更新时间
|
||||
created: string | Dayjs; // 创建时间
|
||||
}
|
||||
|
||||
// 用户注销提示信息 API
|
||||
export const WarningMessageApi = {
|
||||
// 查询用户注销提示信息分页
|
||||
getWarningMessagePage: async (params: any) => {
|
||||
return await request.get({ url: `/keyboard/warning-message/page`, params })
|
||||
},
|
||||
|
||||
// 查询用户注销提示信息详情
|
||||
getWarningMessage: async (id: number) => {
|
||||
return await request.get({ url: `/keyboard/warning-message/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增用户注销提示信息
|
||||
createWarningMessage: async (data: WarningMessage) => {
|
||||
return await request.post({ url: `/keyboard/warning-message/create`, data })
|
||||
},
|
||||
|
||||
// 修改用户注销提示信息
|
||||
updateWarningMessage: async (data: WarningMessage) => {
|
||||
return await request.put({ url: `/keyboard/warning-message/update`, data })
|
||||
},
|
||||
|
||||
// 删除用户注销提示信息
|
||||
deleteWarningMessage: async (id: number) => {
|
||||
return await request.delete({ url: `/keyboard/warning-message/delete?id=` + id })
|
||||
},
|
||||
|
||||
/** 批量删除用户注销提示信息 */
|
||||
deleteWarningMessageList: async (ids: number[]) => {
|
||||
return await request.delete({ url: `/keyboard/warning-message/delete-list?ids=${ids.join(',')}` })
|
||||
},
|
||||
|
||||
// 导出用户注销提示信息 Excel
|
||||
exportWarningMessage: async (params) => {
|
||||
return await request.download({ url: `/keyboard/warning-message/export-excel`, params })
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,11 @@
|
||||
<el-avatar :size="60">
|
||||
<Icon icon="ep:avatar" :size="60" />
|
||||
</el-avatar>
|
||||
<span class="text-18px font-bold">芋道源码</span>
|
||||
<span class="text-18px font-bold">keyLove</span>
|
||||
</div>
|
||||
<Icon icon="tdesign:qrcode" :size="20" />
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center justify-between justify-between bg-white p-x-20px p-y-8px text-12px"
|
||||
>
|
||||
<div class="flex items-center justify-between justify-between bg-white p-x-20px p-y-8px text-12px">
|
||||
<span class="color-#ff690d">点击绑定手机号</span>
|
||||
<span class="rounded-26px bg-#ff6100 p-x-8px p-y-5px color-white">去绑定</span>
|
||||
</div>
|
||||
|
||||
@@ -32,11 +32,7 @@
|
||||
<XTextButton title="预览JSON" @click="previewProcessJson" />
|
||||
</template>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
v-if="props.simulation"
|
||||
effect="light"
|
||||
:content="simulationStatus ? '退出模拟' : '开启模拟'"
|
||||
>
|
||||
<el-tooltip v-if="props.simulation" effect="light" :content="simulationStatus ? '退出模拟' : '开启模拟'">
|
||||
<XButton preIcon="ep:cpu" title="模拟" @click="processSimulation" />
|
||||
</el-tooltip>
|
||||
</ElButtonGroup>
|
||||
@@ -47,11 +43,7 @@
|
||||
icon="el-icon-s-data"
|
||||
@click="elementsAlign('left')"
|
||||
/> -->
|
||||
<XButton
|
||||
preIcon="fa:align-left"
|
||||
class="align align-bottom"
|
||||
@click="elementsAlign('left')"
|
||||
/>
|
||||
<XButton preIcon="fa:align-left" class="align align-bottom" @click="elementsAlign('left')" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="light" content="向右对齐">
|
||||
<!-- <el-button
|
||||
@@ -59,11 +51,7 @@
|
||||
icon="el-icon-s-data"
|
||||
@click="elementsAlign('right')"
|
||||
/> -->
|
||||
<XButton
|
||||
preIcon="fa:align-left"
|
||||
class="align align-top"
|
||||
@click="elementsAlign('right')"
|
||||
/>
|
||||
<XButton preIcon="fa:align-left" class="align align-top" @click="elementsAlign('right')" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="light" content="向上对齐">
|
||||
<!-- <el-button
|
||||
@@ -71,11 +59,7 @@
|
||||
icon="el-icon-s-data"
|
||||
@click="elementsAlign('top')"
|
||||
/> -->
|
||||
<XButton
|
||||
preIcon="fa:align-left"
|
||||
class="align align-left"
|
||||
@click="elementsAlign('top')"
|
||||
/>
|
||||
<XButton preIcon="fa:align-left" class="align align-left" @click="elementsAlign('top')" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="light" content="向下对齐">
|
||||
<!-- <el-button
|
||||
@@ -83,11 +67,7 @@
|
||||
icon="el-icon-s-data"
|
||||
@click="elementsAlign('bottom')"
|
||||
/> -->
|
||||
<XButton
|
||||
preIcon="fa:align-left"
|
||||
class="align align-right"
|
||||
@click="elementsAlign('bottom')"
|
||||
/>
|
||||
<XButton preIcon="fa:align-left" class="align align-right" @click="elementsAlign('bottom')" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="light" content="水平居中">
|
||||
<!-- <el-button
|
||||
@@ -96,11 +76,7 @@
|
||||
@click="elementsAlign('center')"
|
||||
/> -->
|
||||
<!-- class="align align-center" -->
|
||||
<XButton
|
||||
preIcon="fa:align-left"
|
||||
class="align align-center"
|
||||
@click="elementsAlign('center')"
|
||||
/>
|
||||
<XButton preIcon="fa:align-left" class="align align-center" @click="elementsAlign('center')" />
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="light" content="垂直居中">
|
||||
<!-- <el-button
|
||||
@@ -108,11 +84,7 @@
|
||||
icon="el-icon-s-data"
|
||||
@click="elementsAlign('middle')"
|
||||
/> -->
|
||||
<XButton
|
||||
preIcon="fa:align-left"
|
||||
class="align align-middle"
|
||||
@click="elementsAlign('middle')"
|
||||
/>
|
||||
<XButton preIcon="fa:align-left" class="align align-middle" @click="elementsAlign('middle')" />
|
||||
</el-tooltip>
|
||||
</ElButtonGroup>
|
||||
<ElButtonGroup key="scale-control">
|
||||
@@ -122,11 +94,7 @@
|
||||
icon="el-icon-zoom-out"
|
||||
@click="processZoomOut()"
|
||||
/> -->
|
||||
<XButton
|
||||
preIcon="ep:zoom-out"
|
||||
@click="processZoomOut()"
|
||||
:disabled="defaultZoom < 0.2"
|
||||
/>
|
||||
<XButton preIcon="ep:zoom-out" @click="processZoomOut()" :disabled="defaultZoom < 0.2" />
|
||||
</el-tooltip>
|
||||
<el-button>{{ Math.floor(defaultZoom * 10 * 10) + '%' }}</el-button>
|
||||
<el-tooltip effect="light" content="放大视图">
|
||||
@@ -162,32 +130,16 @@
|
||||
</ElButtonGroup>
|
||||
</template>
|
||||
<!-- 用于打开本地文件-->
|
||||
<input
|
||||
type="file"
|
||||
id="files"
|
||||
ref="refFile"
|
||||
style="display: none"
|
||||
accept=".xml, .bpmn"
|
||||
@change="importLocalFile"
|
||||
/>
|
||||
<input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn"
|
||||
@change="importLocalFile" />
|
||||
</div>
|
||||
<div class="my-process-designer__container">
|
||||
<div
|
||||
class="my-process-designer__canvas"
|
||||
ref="bpmnCanvas"
|
||||
id="bpmnCanvas"
|
||||
style="width: 1680px; height: 800px"
|
||||
></div>
|
||||
<div class="my-process-designer__canvas" ref="bpmnCanvas" id="bpmnCanvas" style="width: 1680px; height: 800px">
|
||||
</div>
|
||||
<!-- <div id="js-properties-panel" class="panel"></div> -->
|
||||
<!-- <div class="my-process-designer__canvas" ref="bpmn-canvas"></div> -->
|
||||
</div>
|
||||
<Dialog
|
||||
title="预览"
|
||||
v-model="previewModelVisible"
|
||||
width="80%"
|
||||
:scroll="true"
|
||||
max-height="600px"
|
||||
>
|
||||
<Dialog title="预览" v-model="previewModelVisible" width="80%" :scroll="true" max-height="600px">
|
||||
<div>
|
||||
<pre><code v-dompurify-html="highlightedCode(previewResult)" class="hljs"></code></pre>
|
||||
</div>
|
||||
@@ -542,8 +494,7 @@ const setEncoded = (type, data) => {
|
||||
const encodedData = encodeURIComponent(data)
|
||||
return {
|
||||
filename: `${filename}.${type}`,
|
||||
href: `data:application/${
|
||||
type === 'svg' ? 'text/xml' : 'bpmn20-xml'
|
||||
href: `data:application/${type === 'svg' ? 'text/xml' : 'bpmn20-xml'
|
||||
};charset=UTF-8,${encodedData}`,
|
||||
data: data
|
||||
}
|
||||
@@ -642,7 +593,7 @@ const previewProcessJson = () => {
|
||||
})
|
||||
}
|
||||
|
||||
/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
|
||||
/* ------------------------------------------------ keyLove methods ------------------------------------------------------ */
|
||||
onMounted(() => {
|
||||
initBpmnModeler()
|
||||
createNewDiagram(props.value)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Created by 芋道源码
|
||||
* Created by keyLove
|
||||
*
|
||||
* 枚举类
|
||||
*/
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
<template>
|
||||
<div
|
||||
:class="prefixCls"
|
||||
class="relative h-[100%] lt-md:px-10px lt-sm:px-10px lt-xl:px-10px lt-xl:px-10px"
|
||||
>
|
||||
<div :class="prefixCls" class="relative h-[100%] lt-md:px-10px lt-sm:px-10px lt-xl:px-10px lt-xl:px-10px">
|
||||
<div class="relative mx-auto h-full flex">
|
||||
<div
|
||||
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`"
|
||||
>
|
||||
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`">
|
||||
<!-- 左上角的 logo + 系统标题 -->
|
||||
<div class="relative flex items-center text-white">
|
||||
<img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
|
||||
@@ -14,11 +10,7 @@
|
||||
</div>
|
||||
<!-- 左边的背景图 + 欢迎语 -->
|
||||
<div class="h-[calc(100%-60px)] flex items-center justify-center">
|
||||
<TransitionGroup
|
||||
appear
|
||||
enter-active-class="animate__animated animate__bounceInLeft"
|
||||
tag="div"
|
||||
>
|
||||
<TransitionGroup appear enter-active-class="animate__animated animate__bounceInLeft" tag="div">
|
||||
<img key="1" alt="" class="w-350px" src="@/assets/svgs/login-box-bg.svg" />
|
||||
<div key="2" class="text-3xl text-white">{{ t('login.welcome') }}</div>
|
||||
<div key="3" class="mt-5 text-14px font-normal text-white">
|
||||
@@ -28,12 +20,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px overflow-x-hidden overflow-y-auto"
|
||||
>
|
||||
class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px overflow-x-hidden overflow-y-auto">
|
||||
<!-- 右上角的主题、语言选择 -->
|
||||
<div
|
||||
class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end"
|
||||
>
|
||||
<div class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end">
|
||||
<div class="flex items-center at-2xl:hidden at-xl:hidden">
|
||||
<img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
|
||||
<span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
|
||||
@@ -46,19 +35,10 @@
|
||||
<!-- 右边的登录界面 -->
|
||||
<Transition appear enter-active-class="animate__animated animate__bounceInRight">
|
||||
<div
|
||||
class="m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
|
||||
>
|
||||
class="m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px">
|
||||
<!-- 账号登录 -->
|
||||
<el-form
|
||||
v-show="getShow"
|
||||
ref="formLogin"
|
||||
:model="loginData.loginForm"
|
||||
:rules="LoginRules"
|
||||
class="login-form"
|
||||
label-position="top"
|
||||
label-width="120px"
|
||||
size="large"
|
||||
>
|
||||
<el-form v-show="getShow" ref="formLogin" :model="loginData.loginForm" :rules="LoginRules"
|
||||
class="login-form" label-position="top" label-width="120px" size="large">
|
||||
<el-row style="margin-right: -10px; margin-left: -10px">
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
@@ -67,45 +47,28 @@
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item v-if="loginData.tenantEnable" prop="tenantName">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.tenantName"
|
||||
:placeholder="t('login.tenantNamePlaceholder')"
|
||||
:prefix-icon="iconHouse"
|
||||
link
|
||||
type="primary"
|
||||
/>
|
||||
<el-input v-model="loginData.loginForm.tenantName" :placeholder="t('login.tenantNamePlaceholder')"
|
||||
:prefix-icon="iconHouse" link type="primary" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.username"
|
||||
:placeholder="t('login.usernamePlaceholder')"
|
||||
:prefix-icon="iconAvatar"
|
||||
/>
|
||||
<el-input v-model="loginData.loginForm.username" :placeholder="t('login.usernamePlaceholder')"
|
||||
:prefix-icon="iconAvatar" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.password"
|
||||
:placeholder="t('login.passwordPlaceholder')"
|
||||
:prefix-icon="iconLock"
|
||||
show-password
|
||||
type="password"
|
||||
@keyup.enter="getCode()"
|
||||
/>
|
||||
<el-input v-model="loginData.loginForm.password" :placeholder="t('login.passwordPlaceholder')"
|
||||
:prefix-icon="iconLock" show-password type="password" @keyup.enter="getCode()" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col
|
||||
:span="24"
|
||||
style="
|
||||
<el-col :span="24" style="
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
margin-top: -20px;
|
||||
margin-bottom: -20px;
|
||||
"
|
||||
>
|
||||
">
|
||||
<el-form-item>
|
||||
<el-row justify="space-between" style="width: 100%">
|
||||
<el-col :span="6">
|
||||
@@ -114,8 +77,7 @@
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
<el-col :offset="6" :span="12">
|
||||
<el-link style="float: right" type="primary"
|
||||
>{{ t('login.forgetPassword') }}
|
||||
<el-link style="float: right" type="primary">{{ t('login.forgetPassword') }}
|
||||
</el-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -123,23 +85,12 @@
|
||||
</el-col>
|
||||
<el-col :span="24" style="padding-right: 10px; padding-left: 10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.login')"
|
||||
class="w-[100%]"
|
||||
type="primary"
|
||||
@click="getCode()"
|
||||
/>
|
||||
<XButton :loading="loginLoading" :title="t('login.login')" class="w-[100%]" type="primary"
|
||||
@click="getCode()" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<Verify
|
||||
v-if="loginData.captchaEnable === 'true'"
|
||||
ref="verify"
|
||||
:captchaType="captchaType"
|
||||
:imgSize="{ width: '400px', height: '200px' }"
|
||||
mode="pop"
|
||||
@success="handleLogin"
|
||||
/>
|
||||
<Verify v-if="loginData.captchaEnable === 'true'" ref="verify" :captchaType="captchaType"
|
||||
:imgSize="{ width: '400px', height: '200px' }" mode="pop" @success="handleLogin" />
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
@@ -199,7 +150,7 @@ const loginData = reactive({
|
||||
captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE !== 'false',
|
||||
tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE !== 'false',
|
||||
loginForm: {
|
||||
tenantName: '芋道源码',
|
||||
tenantName: 'keyLove',
|
||||
username: 'admin',
|
||||
password: 'admin123',
|
||||
captchaVerification: '',
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
<template>
|
||||
<el-form
|
||||
v-show="getShow"
|
||||
ref="formSmsLogin"
|
||||
:model="loginData.loginForm"
|
||||
:rules="rules"
|
||||
class="login-form"
|
||||
label-position="top"
|
||||
label-width="120px"
|
||||
size="large"
|
||||
>
|
||||
<el-form v-show="getShow" ref="formSmsLogin" :model="loginData.loginForm" :rules="rules" class="login-form"
|
||||
label-position="top" label-width="120px" size="large">
|
||||
<el-row class="mx-[-10px]">
|
||||
<!-- 租户名 -->
|
||||
<el-col :span="24" class="px-10px">
|
||||
@@ -18,23 +10,15 @@
|
||||
</el-col>
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.tenantName"
|
||||
:placeholder="t('login.tenantNamePlaceholder')"
|
||||
:prefix-icon="iconHouse"
|
||||
type="primary"
|
||||
link
|
||||
/>
|
||||
<el-input v-model="loginData.loginForm.tenantName" :placeholder="t('login.tenantNamePlaceholder')"
|
||||
:prefix-icon="iconHouse" type="primary" link />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 手机号 -->
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item prop="mobileNumber">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.mobileNumber"
|
||||
:placeholder="t('login.mobileNumberPlaceholder')"
|
||||
:prefix-icon="iconCellphone"
|
||||
/>
|
||||
<el-input v-model="loginData.loginForm.mobileNumber" :placeholder="t('login.mobileNumberPlaceholder')"
|
||||
:prefix-icon="iconCellphone" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 验证码 -->
|
||||
@@ -42,19 +26,11 @@
|
||||
<el-form-item prop="code">
|
||||
<el-row :gutter="5" justify="space-between" style="width: 100%">
|
||||
<el-col :span="24">
|
||||
<el-input
|
||||
v-model="loginData.loginForm.code"
|
||||
:placeholder="t('login.codePlaceholder')"
|
||||
:prefix-icon="iconCircleCheck"
|
||||
>
|
||||
<el-input v-model="loginData.loginForm.code" :placeholder="t('login.codePlaceholder')"
|
||||
:prefix-icon="iconCircleCheck">
|
||||
<!-- <el-button class="w-[100%]"> -->
|
||||
<template #append>
|
||||
<span
|
||||
v-if="mobileCodeTimer <= 0"
|
||||
class="getMobileCode"
|
||||
style="cursor: pointer"
|
||||
@click="getSmsCode"
|
||||
>
|
||||
<span v-if="mobileCodeTimer <= 0" class="getMobileCode" style="cursor: pointer" @click="getSmsCode">
|
||||
{{ t('login.getSmsCode') }}
|
||||
</span>
|
||||
<span v-if="mobileCodeTimer > 0" class="getMobileCode" style="cursor: pointer">
|
||||
@@ -70,23 +46,12 @@
|
||||
<!-- 登录按钮 / 返回按钮 -->
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.login')"
|
||||
class="w-full"
|
||||
type="primary"
|
||||
@click="signIn()"
|
||||
/>
|
||||
<XButton :loading="loginLoading" :title="t('login.login')" class="w-full" type="primary" @click="signIn()" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item>
|
||||
<XButton
|
||||
:loading="loginLoading"
|
||||
:title="t('login.backLogin')"
|
||||
class="w-full"
|
||||
@click="handleBackLogin()"
|
||||
/>
|
||||
<XButton :loading="loginLoading" :title="t('login.backLogin')" class="w-full" @click="handleBackLogin()" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -133,7 +98,7 @@ const loginData = reactive({
|
||||
},
|
||||
loginForm: {
|
||||
uuid: '',
|
||||
tenantName: '芋道源码',
|
||||
tenantName: 'keyLove',
|
||||
mobileNumber: '',
|
||||
code: ''
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Created by 芋道源码
|
||||
* Created by keyLove
|
||||
*
|
||||
* AI 枚举类
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Created by 芋道源码
|
||||
* Created by keyLove
|
||||
*
|
||||
* AI 枚举类
|
||||
*
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
||||
<el-form-item label="风格名称" prop="styleName">
|
||||
<el-input v-model="formData.styleName" placeholder="请输入风格名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属地区" prop="local">
|
||||
<el-input v-model="formData.local" placeholder="请输入所属地区" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker
|
||||
v-model="formData.createdAt"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择创建时间"
|
||||
/>
|
||||
<el-date-picker v-model="formData.createdAt" type="date" value-format="x" placeholder="选择创建时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-input v-model="formData.updatedAt" placeholder="请输入更新时间" />
|
||||
@@ -44,6 +36,7 @@ const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
styleName: undefined,
|
||||
local: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
})
|
||||
|
||||
@@ -1,68 +1,36 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
|
||||
<el-form-item label="风格名称" prop="styleName">
|
||||
<el-input
|
||||
v-model="queryParams.styleName"
|
||||
placeholder="请输入风格名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
<el-input v-model="queryParams.styleName" placeholder="请输入风格名称" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createdAt"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="选择创建时间"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
/>
|
||||
<el-date-picker v-model="queryParams.createdAt" value-format="YYYY-MM-DD" type="date" placeholder="选择创建时间"
|
||||
clearable class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-input
|
||||
v-model="queryParams.updatedAt"
|
||||
placeholder="请输入更新时间"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
<el-input v-model="queryParams.updatedAt" placeholder="请输入更新时间" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:create']"
|
||||
>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="openForm('create')"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:export']"
|
||||
>
|
||||
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:export']">
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:delete']"
|
||||
>
|
||||
<el-button type="danger" plain :disabled="isEmpty(checkedIds)" @click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:delete']">
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
@@ -71,53 +39,30 @@
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table
|
||||
row-key="id"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange"
|
||||
>
|
||||
<el-table row-key="id" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="主键 Id" align="center" prop="id" />
|
||||
<el-table-column label="风格名称" align="center" prop="styleName" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createdAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="所属地区" align="center" prop="local" />
|
||||
<el-table-column label="创建时间" align="center" prop="createdAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="更新时间" align="center" prop="updatedAt" />
|
||||
<el-table-column label="操作" align="center" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:update']"
|
||||
>
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:delete']"
|
||||
>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard-server:keyboard-theme-styles:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
|
||||
@@ -225,9 +225,9 @@ const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
const genderFormatter = (_row: KeyboardUser, _column: any, value: number) => {
|
||||
if (value === 1) return '男'
|
||||
if (value === 2) return '女'
|
||||
if (value === 3) return '特殊'
|
||||
if (value === 0) return '男'
|
||||
if (value === 1) return '女'
|
||||
if (value === 2) return '两性'
|
||||
return ''
|
||||
}
|
||||
|
||||
@@ -238,9 +238,9 @@ const yesNoFormatter = (_row: KeyboardUser, _column: any, value: boolean) => {
|
||||
}
|
||||
|
||||
const genderTagType = (value: number) => {
|
||||
if (value === 1) return 'success'
|
||||
if (value === 2) return 'warning'
|
||||
if (value === 3) return 'info'
|
||||
if (value === 0) return 'success'
|
||||
if (value === 1) return 'warning'
|
||||
if (value === 2) return 'info'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +1,7 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="700px">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<!-- 基础信息 -->
|
||||
<el-divider content-position="left">基础信息</el-divider>
|
||||
<el-form-item label="角色名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="如:Katie Leona" />
|
||||
</el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="性别" prop="gender">
|
||||
<el-select v-model="formData.gender" placeholder="请选择性别">
|
||||
<el-option label="男" value="male" />
|
||||
<el-option label="女" value="female" />
|
||||
<el-option label="其他" value="other" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="年龄段" prop="ageRange">
|
||||
<el-input v-model="formData.ageRange" placeholder="如:20s、25-30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="头像" prop="avatarUrl">
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
||||
<el-form-item label="角色头像" prop="avatarUrl">
|
||||
<div class="flex items-center gap-12px">
|
||||
<el-avatar v-if="formData.avatarUrl" :src="formData.avatarUrl" :size="50" />
|
||||
<el-button type="primary" plain @click="avatarInputRef?.click()">
|
||||
@@ -39,32 +10,20 @@
|
||||
<el-button v-if="formData.avatarUrl" type="danger" plain @click="formData.avatarUrl = undefined">
|
||||
删除
|
||||
</el-button>
|
||||
<input
|
||||
ref="avatarInputRef"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
class="hidden"
|
||||
@change="onAvatarFileChange"
|
||||
/>
|
||||
<input ref="avatarInputRef" type="file" accept="image/*" class="hidden" @change="onAvatarFileChange" />
|
||||
</div>
|
||||
<!-- 头像裁剪弹窗 -->
|
||||
<Dialog v-model="cropperVisible" title="裁剪头像(50 x 50)" width="800px" :canFullscreen="false">
|
||||
<div class="flex gap-20px">
|
||||
<div class="flex-1">
|
||||
<CropperImage
|
||||
v-if="avatarCropSrc"
|
||||
:src="avatarCropSrc"
|
||||
height="300px"
|
||||
:options="{ aspectRatio: 1, viewMode: 1 }"
|
||||
@ready="onCropperReady"
|
||||
@cropend="onCropend"
|
||||
/>
|
||||
<CropperImage v-if="avatarCropSrc" :src="avatarCropSrc" height="300px"
|
||||
:options="{ aspectRatio: 1, viewMode: 1 }" @ready="onCropperReady" @cropend="onCropend" />
|
||||
</div>
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<div class="w-50px h-50px overflow-hidden border border-gray-300 rounded-full">
|
||||
<img v-if="avatarPreview" :src="avatarPreview" class="w-full h-full" />
|
||||
</div>
|
||||
<span class="mt-8px text-12px text-gray-400">50 x 50 预览</span>
|
||||
<span class="mt-8px text-12px text-gray-400">200x 200 预览</span>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -75,77 +34,40 @@
|
||||
</template>
|
||||
</Dialog>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="封面图" prop="coverImageUrl">
|
||||
<UploadImg v-model="formData.coverImageUrl" directory="ai_companion_cover" />
|
||||
<el-form-item label="角色封面图" prop="coverImageUrl">
|
||||
<div class="flex items-center gap-12px">
|
||||
<el-image v-if="formData.coverImageUrl" :src="formData.coverImageUrl" fit="cover" class="cover-image-preview"
|
||||
:preview-src-list="[formData.coverImageUrl]" />
|
||||
<el-button type="primary" plain @click="coverInputRef?.click()">
|
||||
{{ formData.coverImageUrl ? '重新上传' : '选择图片' }}
|
||||
</el-button>
|
||||
<el-button v-if="formData.coverImageUrl" type="danger" plain @click="formData.coverImageUrl = undefined">
|
||||
删除
|
||||
</el-button>
|
||||
<input ref="coverInputRef" type="file" accept="image/*" class="hidden" @change="onCoverFileChange" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="一句话简介" prop="shortDesc">
|
||||
<el-input v-model="formData.shortDesc" placeholder="用于卡片或列表展示" />
|
||||
<el-form-item label="性别" prop="gender">
|
||||
<el-input v-model="formData.gender" placeholder="male / female / other" />
|
||||
</el-form-item>
|
||||
<el-form-item label="详细介绍" prop="introText">
|
||||
<el-input v-model="formData.introText" type="textarea" :rows="3" placeholder="用于角色详情页" />
|
||||
<el-form-item label="年龄段" prop="ageRange">
|
||||
<el-input v-model="formData.ageRange" placeholder="如:20s、25-30" />
|
||||
</el-form-item>
|
||||
<!-- 人设配置 -->
|
||||
<el-divider content-position="left">人设配置</el-divider>
|
||||
<el-form-item label="性格标签" prop="personalityTags">
|
||||
<div class="w-full">
|
||||
<div class="flex flex-wrap">
|
||||
<el-tag
|
||||
v-for="(tag, index) in formData.personalityTags"
|
||||
:key="index"
|
||||
class="mr-5px mb-5px"
|
||||
closable
|
||||
@close="removeTag(index)"
|
||||
>
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
<span v-if="!formData.personalityTags?.length" class="text-gray-400">暂无标签</span>
|
||||
</div>
|
||||
<div class="mt-8px flex items-center gap-8px">
|
||||
<el-input v-model="newTag" placeholder="输入标签后回车" class="!w-200px" @keyup.enter="addTag" />
|
||||
<el-button type="primary" plain @click="addTag">添加</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-input v-model="formData.personalityTags" placeholder="如:温柔、黏人、治愈" />
|
||||
</el-form-item>
|
||||
<el-form-item label="说话风格" prop="speakingStyle">
|
||||
<el-input v-model="formData.speakingStyle" placeholder="如:撒娇型、理性型、活泼型" />
|
||||
</el-form-item>
|
||||
<el-form-item label="系统Prompt" prop="systemPrompt">
|
||||
<el-input
|
||||
v-model="formData.systemPrompt"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
placeholder="定义角色核心人设,仅供 AI 模型使用"
|
||||
/>
|
||||
<el-input type="textarea" v-model="formData.systemPrompt" placeholder="定义角色核心人设" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 语音与开场白 -->
|
||||
<el-divider content-position="left">语音与开场白</el-divider>
|
||||
<el-form-item label="开场白" prop="prologue">
|
||||
<el-input v-model="formData.prologue" type="textarea" :rows="2" placeholder="角色的开场白文案" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开场白音频" prop="prologueAudio">
|
||||
<el-input v-model="formData.prologueAudio" placeholder="开场白音频 URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="音色 ID" prop="voiceId">
|
||||
<el-input v-model="formData.voiceId" placeholder="TTS 音色 ID" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 运营配置 -->
|
||||
<el-divider content-position="left">运营配置</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :value="1">上线</el-radio>
|
||||
<el-radio :value="0">下线</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="可见性" prop="visibility">
|
||||
<el-select v-model="formData.visibility" placeholder="请选择可见性">
|
||||
<el-option label="公开" :value="1" />
|
||||
@@ -153,20 +75,27 @@
|
||||
<el-option label="隐藏" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序权重" prop="sortOrder">
|
||||
<el-input-number v-model="formData.sortOrder" :min="0" controls-position="right" />
|
||||
<el-input v-model="formData.sortOrder" placeholder="数值越大排序越靠前" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="热度评分" prop="popularityScore">
|
||||
<el-input-number v-model="formData.popularityScore" :min="0" :precision="1" controls-position="right" />
|
||||
<el-input v-model="formData.popularityScore" placeholder="用于推荐排序" />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker v-model="formData.createdAt" type="date" value-format="x" placeholder="选择创建时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker v-model="formData.updatedAt" type="date" value-format="x" placeholder="选择更新时间" />
|
||||
</el-form-item> -->
|
||||
<el-form-item label="开场白" prop="prologue">
|
||||
<el-input v-model="formData.prologue" placeholder="请输入开场白" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开场白音频" prop="prologueAudio">
|
||||
<el-input v-model="formData.prologueAudio" placeholder="请输入开场白音频URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="音频Id" prop="voiceId">
|
||||
<el-input v-model="formData.voiceId" placeholder="请输入音频Id" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
@@ -174,52 +103,51 @@
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { AiCompanionApi, AiCompanion } from '@/api/keyboard/aicompanion'
|
||||
import { UploadImg } from '@/components/UploadFile'
|
||||
import { CropperImage } from '@/components/Cropper'
|
||||
import * as FileApi from '@/api/infra/file'
|
||||
import type { Cropper } from 'cropperjs'
|
||||
|
||||
/** AI陪聊角色表,用于定义恋爱/陪伴型虚拟角色的基础信息与人设 表单 */
|
||||
defineOptions({ name: 'AiCompanionForm' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const formLoading = ref(false)
|
||||
const formType = ref('')
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
avatarUrl: undefined,
|
||||
coverImageUrl: undefined,
|
||||
gender: undefined,
|
||||
ageRange: undefined,
|
||||
shortDesc: undefined,
|
||||
introText: undefined,
|
||||
personalityTags: [] as string[],
|
||||
personalityTags: undefined,
|
||||
speakingStyle: undefined,
|
||||
systemPrompt: undefined,
|
||||
status: 1,
|
||||
visibility: 1,
|
||||
sortOrder: 0,
|
||||
popularityScore: 0,
|
||||
status: undefined,
|
||||
visibility: undefined,
|
||||
sortOrder: undefined,
|
||||
popularityScore: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined,
|
||||
prologue: undefined,
|
||||
prologueAudio: undefined,
|
||||
voiceId: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
|
||||
systemPrompt: [{ required: true, message: '系统Prompt不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||||
visibility: [{ required: true, message: '请选择可见性', trigger: 'change' }]
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
|
||||
visibility: [{ required: true, message: '可见性不能为空', trigger: 'blur' }],
|
||||
// createdAt: [{ required: true, message: '创建时间不能为空', trigger: 'blur' }],
|
||||
// updatedAt: [{ required: true, message: '更新时间不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref()
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 头像裁剪上传 */
|
||||
/** 头像上传 */
|
||||
const avatarInputRef = ref<HTMLInputElement>()
|
||||
const cropperVisible = ref(false)
|
||||
const avatarCropSrc = ref('')
|
||||
@@ -227,6 +155,10 @@ const avatarPreview = ref('')
|
||||
const avatarUploading = ref(false)
|
||||
let cropperInstance: Cropper | null = null
|
||||
|
||||
/** 封面图上传 */
|
||||
const coverInputRef = ref<HTMLInputElement>()
|
||||
const coverUploading = ref(false)
|
||||
|
||||
const onAvatarFileChange = (e: Event) => {
|
||||
const file = (e.target as HTMLInputElement).files?.[0]
|
||||
if (!file) return
|
||||
@@ -240,15 +172,18 @@ const onAvatarFileChange = (e: Event) => {
|
||||
// 清空 input 以便重复选择同一文件
|
||||
; (e.target as HTMLInputElement).value = ''
|
||||
}
|
||||
|
||||
const onCropperReady = (cropper: Cropper) => {
|
||||
cropperInstance = cropper
|
||||
}
|
||||
|
||||
const onCropend = ({ imgBase64 }: { imgBase64: string }) => {
|
||||
avatarPreview.value = imgBase64
|
||||
}
|
||||
|
||||
const confirmCropAvatar = async () => {
|
||||
if (!cropperInstance) return
|
||||
const canvas = cropperInstance.getCroppedCanvas({ width: 50, height: 50 })
|
||||
const canvas = cropperInstance.getCroppedCanvas({ width: 200, height: 200 })
|
||||
canvas.toBlob(async (blob) => {
|
||||
if (!blob) return
|
||||
avatarUploading.value = true
|
||||
@@ -264,32 +199,20 @@ const confirmCropAvatar = async () => {
|
||||
}, 'image/png')
|
||||
}
|
||||
|
||||
/** 性格标签 */
|
||||
const newTag = ref('')
|
||||
const normalizePersonalityTags = (raw: unknown): string[] => {
|
||||
if (!raw) return []
|
||||
if (Array.isArray(raw)) return raw.map(String)
|
||||
if (typeof raw === 'string') {
|
||||
const onCoverFileChange = async (e: Event) => {
|
||||
const file = (e.target as HTMLInputElement).files?.[0]
|
||||
if (!file) return
|
||||
coverUploading.value = true
|
||||
try {
|
||||
const parsed = JSON.parse(raw)
|
||||
if (Array.isArray(parsed)) return parsed.map(String)
|
||||
} catch {
|
||||
return raw.split(/[,,]/).map((s) => s.trim()).filter(Boolean)
|
||||
const res = await FileApi.updateFile({ file, directory: 'ai_companion_cover' })
|
||||
formData.value.coverImageUrl = res.data
|
||||
message.success('封面图上传成功')
|
||||
} finally {
|
||||
coverUploading.value = false
|
||||
// 清空 input 以便重复选择同一文件
|
||||
; (e.target as HTMLInputElement).value = ''
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
const addTag = () => {
|
||||
const text = newTag.value.trim()
|
||||
if (!text) return
|
||||
if (!formData.value.personalityTags.includes(text)) {
|
||||
formData.value.personalityTags = [...formData.value.personalityTags, text]
|
||||
}
|
||||
newTag.value = ''
|
||||
}
|
||||
const removeTag = (index: number) => {
|
||||
formData.value.personalityTags = formData.value.personalityTags.filter((_, i) => i !== index)
|
||||
}
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
@@ -297,25 +220,24 @@ const open = async (type: string, id?: number) => {
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = await AiCompanionApi.getAiCompanion(id)
|
||||
formData.value = {
|
||||
...data,
|
||||
personalityTags: normalizePersonalityTags(data.personalityTags)
|
||||
}
|
||||
formData.value = await AiCompanionApi.getAiCompanion(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open })
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success'])
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as AiCompanion
|
||||
@@ -327,6 +249,7 @@ const submitForm = async () => {
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
@@ -337,20 +260,19 @@ const submitForm = async () => {
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
avatarUrl: undefined,
|
||||
coverImageUrl: undefined,
|
||||
gender: undefined,
|
||||
ageRange: undefined,
|
||||
shortDesc: undefined,
|
||||
introText: undefined,
|
||||
personalityTags: [] as string[],
|
||||
personalityTags: undefined,
|
||||
speakingStyle: undefined,
|
||||
systemPrompt: undefined,
|
||||
status: 1,
|
||||
visibility: 1,
|
||||
sortOrder: 0,
|
||||
popularityScore: 0,
|
||||
status: undefined,
|
||||
visibility: undefined,
|
||||
sortOrder: undefined,
|
||||
popularityScore: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined,
|
||||
prologue: undefined,
|
||||
prologueAudio: undefined,
|
||||
voiceId: undefined
|
||||
@@ -358,3 +280,22 @@ const resetForm = () => {
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cover-image-preview {
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 表单布局优化 */
|
||||
.el-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* 按钮间距 */
|
||||
.el-button {
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
||||
<!-- <el-form-item label="主表ID" prop="companionId">
|
||||
<el-input v-model="formData.companionId" placeholder="请输入主表ID" />
|
||||
</el-form-item> -->
|
||||
<el-form-item label="语言标识" prop="locale">
|
||||
<el-input v-model="formData.locale" placeholder="请选择语言标识" />
|
||||
<!-- <el-select v-model="formData.locale" placeholder="请选择语言标识">
|
||||
<el-option label="中文(简体)" value="zh-CN" />
|
||||
<el-option label="英文(美国)" value="en-US" />
|
||||
<el-option label="日文" value="ja-JP" />
|
||||
<el-option label="韩文" value="ko-KR" />
|
||||
<el-option label="法文" value="fr-FR" />
|
||||
<el-option label="德文" value="de-DE" />
|
||||
<el-option label="西班牙文" value="es-ES" />
|
||||
</el-select> -->
|
||||
</el-form-item>
|
||||
<el-form-item label="角色名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入角色名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="一句话描述" prop="shortDesc">
|
||||
<el-input v-model="formData.shortDesc" placeholder="请输入一句话人设描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="详细介绍" prop="introText">
|
||||
<el-input v-model="formData.introText" placeholder="请输入角色详细介绍" type="textarea" :rows="4" />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker v-model="formData.createdAt" type="date" value-format="x" placeholder="选择创建时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker v-model="formData.updatedAt" type="date" value-format="x" placeholder="选择更新时间" />
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { AiCompanionApi, AiCompanionI18n } from '@/api/keyboard/aicompanion'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
companionId: undefined,
|
||||
locale: undefined,
|
||||
name: undefined,
|
||||
shortDesc: undefined,
|
||||
introText: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
locale: [{ required: true, message: '语言标识不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
formData.value.id = id as any
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await AiCompanionApi.getAiCompanionI18n(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as AiCompanionI18n
|
||||
if (formType.value === 'create') {
|
||||
await AiCompanionApi.createAiCompanionI18n(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await AiCompanionApi.updateAiCompanionI18n(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
companionId: undefined,
|
||||
locale: undefined,
|
||||
name: undefined,
|
||||
shortDesc: undefined,
|
||||
introText: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 表单布局优化 */
|
||||
.el-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* 按钮间距 */
|
||||
.el-button {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* 输入框宽度 */
|
||||
.el-input {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['keyboard:ai-companion:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button type="danger" plain :disabled="isEmpty(checkedIds)" @click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:ai-companion:delete']">
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
<el-table row-key="id" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="主表ID" align="center" prop="companionId" width="120px" />
|
||||
<el-table-column label="语言标识" align="center" prop="locale" width="120px" />
|
||||
<el-table-column label="角色名称" align="center" prop="name" width="180px" />
|
||||
<el-table-column label="一句话描述" align="center" prop="shortDesc" width="200px" />
|
||||
<el-table-column label="详细介绍" align="center" prop="introText" min-width="300px" />
|
||||
<el-table-column label="创建时间" align="center" prop="createdAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="更新时间" align="center" prop="updatedAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard:ai-companion:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard:ai-companion:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</ContentWrap>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<AiCompanionI18nForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { AiCompanionApi, AiCompanionI18n } from '@/api/keyboard/aicompanion'
|
||||
import AiCompanionI18nForm from './AiCompanionI18nForm.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps<{
|
||||
id?: number // 主键ID(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
id: undefined as unknown
|
||||
})
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.id,
|
||||
(val: number) => {
|
||||
if (!val) {
|
||||
list.value = [] // 清空列表
|
||||
return
|
||||
}
|
||||
queryParams.id = val
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await AiCompanionApi.getAiCompanionI18nPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
if (!props.id) {
|
||||
message.error('请选择一个AI陪聊角色表,用于定义恋爱/陪伴型虚拟角色的基础信息与人设')
|
||||
return
|
||||
}
|
||||
formRef.value.open(type, id, props.id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await AiCompanionApi.deleteAiCompanionI18n(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/** 批量删除AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍 */
|
||||
const handleDeleteBatch = async () => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await AiCompanionApi.deleteAiCompanionI18nList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList();
|
||||
} catch { }
|
||||
}
|
||||
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (records: AiCompanionI18n[]) => {
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 按钮间距 */
|
||||
.el-button {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 表格样式优化 */
|
||||
.el-table {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/* 操作按钮间距 */
|
||||
.el-table-column__content .el-button {
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,80 +1,92 @@
|
||||
<template>
|
||||
<!-- 搜索 -->
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="角色名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入角色名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
|
||||
<el-form-item label="角色头像" prop="avatarUrl">
|
||||
<el-input v-model="queryParams.avatarUrl" placeholder="请输入角色头像URL" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色封面图" prop="coverImageUrl">
|
||||
<el-input v-model="queryParams.coverImageUrl" placeholder="请输入角色封面图URL" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="gender">
|
||||
<el-select v-model="queryParams.gender" placeholder="全部" clearable class="!w-240px">
|
||||
<el-option label="男" value="male" />
|
||||
<el-option label="女" value="female" />
|
||||
<el-option label="其他" value="other" />
|
||||
</el-select>
|
||||
<el-input v-model="queryParams.gender" placeholder="male / female / other" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="年龄段" prop="ageRange">
|
||||
<el-input v-model="queryParams.ageRange" placeholder="如:20s、25-30" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性格标签" prop="personalityTags">
|
||||
<el-input v-model="queryParams.personalityTags" placeholder="如:温柔、黏人、治愈" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="说话风格" prop="speakingStyle">
|
||||
<el-input v-model="queryParams.speakingStyle" placeholder="如:撒娇型、理性型、活泼型" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="系统Prompt" prop="systemPrompt">
|
||||
<el-input v-model="queryParams.systemPrompt" placeholder="定义角色核心人设" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="全部" clearable class="!w-240px">
|
||||
<el-option label="上线" :value="1" />
|
||||
<el-option label="下线" :value="0" />
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px">
|
||||
<el-option label="上线" value="1" />
|
||||
<el-option label="下线" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="可见性" prop="visibility">
|
||||
<el-select v-model="queryParams.visibility" placeholder="全部" clearable class="!w-240px">
|
||||
<el-option label="公开" :value="1" />
|
||||
<el-option label="内测" :value="2" />
|
||||
<el-option label="隐藏" :value="3" />
|
||||
<el-select v-model="queryParams.visibility" placeholder="请选择可见性" clearable class="!w-240px">
|
||||
<el-option label="公开" value="1" />
|
||||
<el-option label="内测" value="2" />
|
||||
<el-option label="隐藏" value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
<el-form-item label="排序权重" prop="sortOrder">
|
||||
<el-input v-model="queryParams.sortOrder" placeholder="数值越大排序越靠前" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="热度评分" prop="popularityScore">
|
||||
<el-input v-model="queryParams.popularityScore" placeholder="用于推荐排序" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker v-model="queryParams.createdAt" value-format="YYYY-MM-DD" type="date" placeholder="选择创建时间"
|
||||
clearable class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker v-model="queryParams.updatedAt" value-format="YYYY-MM-DD" type="date" placeholder="选择更新时间"
|
||||
clearable class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开场白" prop="prologue">
|
||||
<el-input v-model="queryParams.prologue" placeholder="请输入开场白" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开场白音频" prop="prologueAudio">
|
||||
<el-input v-model="queryParams.prologueAudio" placeholder="请输入开场白音频" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="音频Id" prop="voiceId">
|
||||
<el-input v-model="queryParams.voiceId" placeholder="请输入音频Id" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['keyboard:ai-companion:create']"
|
||||
>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['keyboard:ai-companion:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['keyboard:ai-companion:export']"
|
||||
>
|
||||
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
|
||||
v-hasPermi="['keyboard:ai-companion:export']">
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:ai-companion:delete']"
|
||||
>
|
||||
<el-button type="danger" plain :disabled="isEmpty(checkedIds)" @click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:ai-companion:delete']">
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
@@ -83,93 +95,64 @@
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table
|
||||
row-key="id"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange"
|
||||
>
|
||||
<el-table row-key="id" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
|
||||
highlight-current-row @current-change="handleCurrentChange" @selection-change="handleRowCheckboxChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="ID" align="center" prop="id" width="70" />
|
||||
<el-table-column label="头像" align="center" prop="avatarUrl" width="80">
|
||||
<el-table-column label="陪聊角色唯一ID" align="center" prop="id" width="120px" />
|
||||
<el-table-column label="角色头像" align="center" prop="avatarUrl" width="100px">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
v-if="scope.row.avatarUrl"
|
||||
:src="scope.row.avatarUrl"
|
||||
:preview-src-list="[scope.row.avatarUrl]"
|
||||
preview-teleported
|
||||
fit="cover"
|
||||
class="w-40px h-40px rounded-full"
|
||||
/>
|
||||
<el-image v-if="scope.row.avatarUrl" :src="scope.row.avatarUrl" fit="cover" class="avatar-image" />
|
||||
<span v-else>无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="角色名称" align="center" prop="name" min-width="120" />
|
||||
<el-table-column label="性别" align="center" prop="gender" width="70">
|
||||
<el-table-column label="角色封面图" align="center" prop="coverImageUrl" width="150px">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.gender === 'male'" type="primary">男</el-tag>
|
||||
<el-tag v-else-if="scope.row.gender === 'female'" type="danger">女</el-tag>
|
||||
<el-tag v-else type="info">其他</el-tag>
|
||||
<el-image v-if="scope.row.coverImageUrl" :src="scope.row.coverImageUrl" fit="cover" class="cover-image" />
|
||||
<span v-else>无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="年龄段" align="center" prop="ageRange" width="80" />
|
||||
<el-table-column label="简介" align="center" prop="shortDesc" min-width="160" />
|
||||
<el-table-column label="说话风格" align="center" prop="speakingStyle" width="100" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="80">
|
||||
<el-table-column label="角色性别(male / female / other)" align="center" prop="gender" />
|
||||
<el-table-column label="角色年龄段描述(如:20s、25-30)" align="center" prop="ageRange" />
|
||||
<el-table-column label="角色性格标签数组(如:温柔、黏人、治愈)" align="center" prop="personalityTags" />
|
||||
<el-table-column label="角色说话风格(如:撒娇型、理性型、活泼型)" align="center" prop="speakingStyle" />
|
||||
<el-table-column label="AI系统Prompt,定义角色核心人设,仅供模型使用" align="center" prop="systemPrompt" />
|
||||
<el-table-column label="角色状态:1=上线,0=下线" align="center" prop="status" />
|
||||
<el-table-column label="角色可见性:1=公开,2=内测,3=隐藏" align="center" prop="visibility" />
|
||||
<el-table-column label="排序权重,数值越大排序越靠前" align="center" prop="sortOrder" />
|
||||
<el-table-column label="角色热度评分,用于推荐排序" align="center" prop="popularityScore" />
|
||||
<!-- <el-table-column label="创建时间" align="center" prop="createdAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="更新时间" align="center" prop="updatedAt" :formatter="dateFormatter" width="180px" /> -->
|
||||
<el-table-column label="开场白" align="center" prop="prologue" />
|
||||
<el-table-column label="开场白音频" align="center" prop="prologueAudio" />
|
||||
<el-table-column label="音频Id" align="center" prop="voiceId" />
|
||||
<el-table-column label="操作" align="center" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status === 1" type="success">上线</el-tag>
|
||||
<el-tag v-else type="info">下线</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="可见性" align="center" prop="visibility" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.visibility === 1">公开</el-tag>
|
||||
<el-tag v-else-if="scope.row.visibility === 2" type="warning">内测</el-tag>
|
||||
<el-tag v-else-if="scope.row.visibility === 3" type="info">隐藏</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="center" prop="sortOrder" width="70" />
|
||||
<el-table-column label="热度" align="center" prop="popularityScore" width="70" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createdAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="130">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard:ai-companion:update']"
|
||||
>
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard:ai-companion:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard:ai-companion:delete']"
|
||||
>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard:ai-companion:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<AiCompanionForm ref="formRef" @success="getList" />
|
||||
<!-- 子表的列表 -->
|
||||
<ContentWrap>
|
||||
<el-tabs model-value="aiCompanionI18n">
|
||||
<el-tab-pane label="AI陪聊角色国际化表,用于存储不同语言下的角色名称、一句话描述和详细介绍" name="aiCompanionI18n">
|
||||
<AiCompanionI18nList :id="currentRow.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -178,26 +161,41 @@ import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { AiCompanionApi, AiCompanion } from '@/api/keyboard/aicompanion'
|
||||
import AiCompanionForm from './AiCompanionForm.vue'
|
||||
import AiCompanionI18nList from './components/AiCompanionI18nList.vue'
|
||||
|
||||
/** AI陪聊角色表,用于定义恋爱/陪伴型虚拟角色的基础信息与人设 列表 */
|
||||
defineOptions({ name: 'KeyboardAiCompanion' })
|
||||
|
||||
const message = useMessage()
|
||||
const { t } = useI18n()
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true)
|
||||
const list = ref<AiCompanion[]>([])
|
||||
const total = ref(0)
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<AiCompanion[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
avatarUrl: undefined,
|
||||
coverImageUrl: undefined,
|
||||
gender: undefined,
|
||||
ageRange: undefined,
|
||||
personalityTags: undefined,
|
||||
speakingStyle: undefined,
|
||||
systemPrompt: undefined,
|
||||
status: undefined,
|
||||
visibility: undefined,
|
||||
createTime: []
|
||||
sortOrder: undefined,
|
||||
popularityScore: undefined,
|
||||
createdAt: undefined,
|
||||
createdAt: [],
|
||||
updatedAt: undefined,
|
||||
updatedAt: [],
|
||||
prologue: undefined,
|
||||
prologueAudio: undefined,
|
||||
voiceId: undefined
|
||||
})
|
||||
const queryFormRef = ref()
|
||||
const exportLoading = ref(false)
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
@@ -232,43 +230,92 @@ const openForm = (type: string, id?: number) => {
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await AiCompanionApi.deleteAiCompanion(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
currentRow.value = {}
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/** 批量删除 */
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (records: AiCompanion[]) => {
|
||||
checkedIds.value = records.map((item) => item.id!)
|
||||
}
|
||||
/** 批量删除AI陪聊角色表,用于定义恋爱/陪伴型虚拟角色的基础信息与人设 */
|
||||
const handleDeleteBatch = async () => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await AiCompanionApi.deleteAiCompanionList(checkedIds.value)
|
||||
checkedIds.value = []
|
||||
await AiCompanionApi.deleteAiCompanionList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList()
|
||||
await getList();
|
||||
} catch { }
|
||||
}
|
||||
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (records: AiCompanion[]) => {
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await AiCompanionApi.exportAiCompanion(queryParams)
|
||||
download.excel(data, 'AI陪聊角色.xls')
|
||||
download.excel(data, 'AI陪聊角色表,用于定义恋爱/陪伴型虚拟角色的基础信息与人设.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 选中行操作 */
|
||||
const currentRow = ref({}) // 选中行
|
||||
const handleCurrentChange = (row) => {
|
||||
currentRow.value = row
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.avatar-image {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 表格行悬停效果 */
|
||||
:deep(.el-table__row:hover) {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
|
||||
/* 图片加载和错误处理 */
|
||||
:deep(.el-image__error) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f0f0f0;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
:deep(.el-image__placeholder) {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
241
src/views/keyboard/appversions/AppVersionsForm.vue
Normal file
241
src/views/keyboard/appversions/AppVersionsForm.vue
Normal file
@@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="760px">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="110px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-divider content-position="left">基础信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="应用标识" prop="appId">
|
||||
<el-input v-model="formData.appId" placeholder="如:main" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="平台" prop="platform">
|
||||
<el-select v-model="formData.platform" placeholder="请选择平台">
|
||||
<el-option label="Android" value="android" />
|
||||
<el-option label="iOS" value="ios" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="渠道" prop="channel">
|
||||
<el-input v-model="formData.channel" placeholder="如:official" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="构建号" prop="buildNumber">
|
||||
<el-input v-model="formData.buildNumber" placeholder="iOS 可选" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">版本策略</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="展示版本" prop="versionName">
|
||||
<el-input v-model="formData.versionName" placeholder="如:1.2.3" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="版本码" prop="versionCode">
|
||||
<el-input-number v-model="formData.versionCode" :min="1" controls-position="right" class="!w-full" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最低版本码" prop="minSupportedCode">
|
||||
<el-input-number v-model="formData.minSupportedCode" :min="1" controls-position="right" class="!w-full" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发布时间" prop="releasedAt">
|
||||
<el-date-picker
|
||||
v-model="formData.releasedAt"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择发布时间"
|
||||
class="!w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="强制更新" prop="isForceUpdate">
|
||||
<el-radio-group v-model="formData.isForceUpdate">
|
||||
<el-radio :value="true">是</el-radio>
|
||||
<el-radio :value="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否生效" prop="isActive">
|
||||
<el-radio-group v-model="formData.isActive">
|
||||
<el-radio :value="true">生效</el-radio>
|
||||
<el-radio :value="false">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">更新内容</el-divider>
|
||||
<el-form-item label="更新说明" prop="releaseNotes">
|
||||
<el-input
|
||||
v-model="formData.releaseNotes"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="展示给用户的更新内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="下载地址" prop="downloadUrl">
|
||||
<el-input v-model="formData.downloadUrl" placeholder="APK 直链 / App Store 链接" />
|
||||
</el-form-item>
|
||||
<el-form-item label="商店链接" prop="storeUrl">
|
||||
<el-input v-model="formData.storeUrl" placeholder="可选" />
|
||||
</el-form-item>
|
||||
<el-form-item label="扩展元数据" prop="metadataText">
|
||||
<el-input
|
||||
v-model="formData.metadataText"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder='JSON 格式,例如:{"size":"20MB","md5":"xxx"}'
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { AppVersionsApi, AppVersions } from '@/api/keyboard/appversions'
|
||||
|
||||
defineOptions({ name: 'AppVersionsForm' })
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const formLoading = ref(false)
|
||||
const formType = ref('')
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
appId: undefined,
|
||||
platform: undefined,
|
||||
channel: undefined,
|
||||
versionName: undefined,
|
||||
versionCode: 1,
|
||||
buildNumber: undefined,
|
||||
minSupportedCode: 1,
|
||||
isForceUpdate: false,
|
||||
isActive: true,
|
||||
releaseNotes: undefined,
|
||||
downloadUrl: undefined,
|
||||
storeUrl: undefined,
|
||||
metadataText: undefined,
|
||||
releasedAt: undefined
|
||||
})
|
||||
|
||||
const formRules = reactive({
|
||||
appId: [{ required: true, message: '应用标识不能为空', trigger: 'blur' }],
|
||||
platform: [{ required: true, message: '请选择平台', trigger: 'change' }],
|
||||
channel: [{ required: true, message: '渠道不能为空', trigger: 'blur' }],
|
||||
versionName: [{ required: true, message: '展示版本不能为空', trigger: 'blur' }],
|
||||
versionCode: [{ required: true, message: '版本码不能为空', trigger: 'change' }],
|
||||
minSupportedCode: [{ required: true, message: '最低版本码不能为空', trigger: 'change' }],
|
||||
isForceUpdate: [{ required: true, message: '请选择是否强制更新', trigger: 'change' }],
|
||||
isActive: [{ required: true, message: '请选择是否生效', trigger: 'change' }],
|
||||
releaseNotes: [{ required: true, message: '更新说明不能为空', trigger: 'blur' }],
|
||||
downloadUrl: [{ required: true, message: '下载地址不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref()
|
||||
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = await AppVersionsApi.getAppVersions(id)
|
||||
formData.value = {
|
||||
...data,
|
||||
versionCode: data.versionCode || 1,
|
||||
minSupportedCode: data.minSupportedCode || 1,
|
||||
isForceUpdate: !!data.isForceUpdate,
|
||||
isActive: !!data.isActive,
|
||||
metadataText: data.metadata ? JSON.stringify(data.metadata, null, 2) : undefined
|
||||
}
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
const submitForm = async () => {
|
||||
await formRef.value.validate()
|
||||
|
||||
let metadata: Record<string, any> | undefined
|
||||
if (formData.value.metadataText) {
|
||||
try {
|
||||
metadata = JSON.parse(formData.value.metadataText)
|
||||
} catch {
|
||||
message.warning('扩展元数据不是有效 JSON,请检查格式')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
formLoading.value = true
|
||||
try {
|
||||
const { metadataText, ...rest } = formData.value
|
||||
const data = { ...rest, metadata } as unknown as AppVersions
|
||||
if (formType.value === 'create') {
|
||||
await AppVersionsApi.createAppVersions(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await AppVersionsApi.updateAppVersions(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
appId: undefined,
|
||||
platform: undefined,
|
||||
channel: undefined,
|
||||
versionName: undefined,
|
||||
versionCode: 1,
|
||||
buildNumber: undefined,
|
||||
minSupportedCode: 1,
|
||||
isForceUpdate: false,
|
||||
isActive: true,
|
||||
releaseNotes: undefined,
|
||||
downloadUrl: undefined,
|
||||
storeUrl: undefined,
|
||||
metadataText: undefined,
|
||||
releasedAt: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
285
src/views/keyboard/appversions/index.vue
Normal file
285
src/views/keyboard/appversions/index.vue
Normal file
@@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<!-- 搜索 -->
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="应用" prop="appId">
|
||||
<el-input
|
||||
v-model="queryParams.appId"
|
||||
placeholder="请输入应用标识"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-220px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="平台" prop="platform">
|
||||
<el-select v-model="queryParams.platform" placeholder="全部" clearable class="!w-160px">
|
||||
<el-option label="Android" value="android" />
|
||||
<el-option label="iOS" value="ios" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="渠道" prop="channel">
|
||||
<el-input
|
||||
v-model="queryParams.channel"
|
||||
placeholder="请输入渠道"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="版本号" prop="versionName">
|
||||
<el-input
|
||||
v-model="queryParams.versionName"
|
||||
placeholder="如 1.2.3"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-160px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="强更" prop="isForceUpdate">
|
||||
<el-select v-model="queryParams.isForceUpdate" placeholder="全部" clearable class="!w-120px">
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="isActive">
|
||||
<el-select v-model="queryParams.isActive" placeholder="全部" clearable class="!w-120px">
|
||||
<el-option label="生效" :value="true" />
|
||||
<el-option label="停用" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="发布时间" prop="releasedAt">
|
||||
<el-date-picker
|
||||
v-model="queryParams.releasedAt"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-260px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['keyboard:app-versions:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['keyboard:app-versions:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:app-versions:delete']"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table
|
||||
row-key="id"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="ID" align="center" prop="id" width="70" />
|
||||
<el-table-column label="应用" align="center" prop="appId" min-width="120" />
|
||||
<el-table-column label="平台" align="center" prop="platform" width="90">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.platform === 'android'" type="success">Android</el-tag>
|
||||
<el-tag v-else-if="scope.row.platform === 'ios'" type="primary">iOS</el-tag>
|
||||
<el-tag v-else type="info">未知</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="渠道" align="center" prop="channel" min-width="120" />
|
||||
<el-table-column label="版本号" align="center" prop="versionName" width="120" />
|
||||
<el-table-column label="版本码" align="center" prop="versionCode" width="100" />
|
||||
<el-table-column label="最低版本" align="center" prop="minSupportedCode" width="100" />
|
||||
<el-table-column label="强更" align="center" prop="isForceUpdate" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isForceUpdate ? 'danger' : 'info'">
|
||||
{{ scope.row.isForceUpdate ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="isActive" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isActive ? 'success' : 'info'">
|
||||
{{ scope.row.isActive ? '生效' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下载地址" align="center" prop="downloadUrl" min-width="180">
|
||||
<template #default="scope">
|
||||
<el-link v-if="scope.row.downloadUrl" :href="scope.row.downloadUrl" target="_blank" type="primary">
|
||||
打开链接
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="发布时间"
|
||||
align="center"
|
||||
prop="releasedAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createdAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="130">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard:app-versions:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard:app-versions:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<AppVersionsForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { AppVersionsApi, AppVersions } from '@/api/keyboard/appversions'
|
||||
import AppVersionsForm from './AppVersionsForm.vue'
|
||||
|
||||
defineOptions({ name: 'AppVersions' })
|
||||
|
||||
const message = useMessage()
|
||||
const { t } = useI18n()
|
||||
|
||||
const loading = ref(true)
|
||||
const list = ref<AppVersions[]>([])
|
||||
const total = ref(0)
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
appId: undefined,
|
||||
platform: undefined,
|
||||
channel: undefined,
|
||||
versionName: undefined,
|
||||
isForceUpdate: undefined,
|
||||
isActive: undefined,
|
||||
releasedAt: []
|
||||
})
|
||||
const queryFormRef = ref()
|
||||
const exportLoading = ref(false)
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await AppVersionsApi.getAppVersionsPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
await message.delConfirm()
|
||||
await AppVersionsApi.deleteAppVersions(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (records: AppVersions[]) => {
|
||||
checkedIds.value = records.map((item) => item.id!)
|
||||
}
|
||||
const handleDeleteBatch = async () => {
|
||||
try {
|
||||
await message.delConfirm()
|
||||
await AppVersionsApi.deleteAppVersionsList(checkedIds.value)
|
||||
checkedIds.value = []
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
await message.exportConfirm()
|
||||
exportLoading.value = true
|
||||
const data = await AppVersionsApi.exportAppVersions(queryParams)
|
||||
download.excel(data, 'App版本管理.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
||||
<el-form-item label="标题" prop="characterName">
|
||||
<!-- <el-form-item label="标题" prop="characterName">
|
||||
<el-input v-model="formData.characterName" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="背景描述" prop="characterBackground">
|
||||
<el-input v-model="formData.characterBackground" placeholder="请输入背景描述" />
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="角色头像" prop="avatarUrl">
|
||||
<UploadImg v-model="formData.avatarUrl" height="120px" width="120px" />
|
||||
</el-form-item>
|
||||
|
||||
117
src/views/keyboard/character/components/CharacterI18nForm.vue
Normal file
117
src/views/keyboard/character/components/CharacterI18nForm.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
|
||||
<el-form-item label="语言标识" prop="locale">
|
||||
<el-input v-model="formData.locale" placeholder="请输入语言标识,如 zh-CN/en-US/ja-JP" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标题" prop="characterName">
|
||||
<el-input v-model="formData.characterName" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="背景描述" prop="characterBackground">
|
||||
<el-input v-model="formData.characterBackground" placeholder="请输入背景描述" />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker
|
||||
v-model="formData.createdAt"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择创建时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker
|
||||
v-model="formData.updatedAt"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择更新时间"
|
||||
/>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { CharacterApi, CharacterI18n } from '@/api/keyboard/character'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗`的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
characterId: undefined,
|
||||
locale: undefined,
|
||||
characterName: undefined,
|
||||
characterBackground: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
characterId: [{ required: true, message: '角色主表id不能为空', trigger: 'blur' }],
|
||||
locale: [{ required: true, message: '语言标识,如 zh-CN/en-US/ja-JP不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number, characterId?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
formData.value.characterId = characterId as any
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await CharacterApi.getCharacterI18n(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as CharacterI18n
|
||||
if (formType.value === 'create') {
|
||||
await CharacterApi.createCharacterI18n(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await CharacterApi.updateCharacterI18n(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
characterId: undefined,
|
||||
locale: undefined,
|
||||
characterName: undefined,
|
||||
characterBackground: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
171
src/views/keyboard/character/components/CharacterI18nList.vue
Normal file
171
src/views/keyboard/character/components/CharacterI18nList.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['keyboard:character:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:character:delete']"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
<el-table
|
||||
row-key="id"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="主键 Id" align="center" prop="id" />
|
||||
<el-table-column label="语言标识,如 zh-CN/en-US/ja-JP" align="center" prop="locale" />
|
||||
<el-table-column label="标题" align="center" prop="characterName" />
|
||||
<el-table-column label="背景描述" align="center" prop="characterBackground" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createdAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="更新时间"
|
||||
align="center"
|
||||
prop="updatedAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard:character:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard:character:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CharacterI18nForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { CharacterApi, CharacterI18n } from '@/api/keyboard/character'
|
||||
import CharacterI18nForm from './CharacterI18nForm.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps<{
|
||||
characterId?: number // 角色主表id(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
characterId: undefined as unknown
|
||||
})
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.characterId,
|
||||
(val: number) => {
|
||||
if (!val) {
|
||||
list.value = [] // 清空列表
|
||||
return
|
||||
}
|
||||
queryParams.characterId = val
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CharacterApi.getCharacterI18nPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
if (!props.characterId) {
|
||||
message.error('请选择一个键盘人设')
|
||||
return
|
||||
}
|
||||
formRef.value.open(type, id, props.characterId)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await CharacterApi.deleteCharacterI18n(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 批量删除键盘人设国际化内容 */
|
||||
const handleDeleteBatch = async () => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await CharacterApi.deleteCharacterI18nList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList();
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (records: CharacterI18n[]) => {
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
</script>
|
||||
@@ -67,15 +67,15 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table row-key="id" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange">
|
||||
highlight-current-row @current-change="handleCurrentChange" @selection-change="handleRowCheckboxChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="主键 Id" align="center" prop="id" />
|
||||
<el-table-column label="标题" align="center" prop="characterName" />
|
||||
<el-table-column label="背景描述" align="center" prop="characterBackground" />
|
||||
<el-table-column label="角色头像" align="center" prop="avatarUrl" width="90px">
|
||||
<template #default="{ row }">
|
||||
<el-image v-if="row.avatarUrl" :src="row.avatarUrl" class="h-60px " @click="imagePreview(row.avatarUrl)" />
|
||||
<span v-else>-</span>
|
||||
<!-- <el-table-column label="标题" align="center" prop="characterName" />
|
||||
<el-table-column label="背景描述" align="center" prop="characterBackground" /> -->
|
||||
<el-table-column label="角色头像" align="center" prop="avatarUrl">
|
||||
<template #default="scope">
|
||||
<el-image v-if="scope.row.avatarUrl" :src="scope.row.avatarUrl" fit="cover" class="character-avatar" />
|
||||
<span v-else>无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下载次数" align="center" prop="download" />
|
||||
@@ -104,6 +104,14 @@
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CharacterForm ref="formRef" @success="getList" />
|
||||
<!-- 子表的列表 -->
|
||||
<ContentWrap>
|
||||
<el-tabs model-value="characterI18n">
|
||||
<el-tab-pane label="键盘人设国际化内容" name="characterI18n">
|
||||
<CharacterI18nList :character-id="currentRow.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -112,7 +120,7 @@ import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { CharacterApi, Character } from '@/api/keyboard/character'
|
||||
import CharacterForm from './CharacterForm.vue'
|
||||
import { createImageViewer } from '@/components/ImageViewer'
|
||||
import CharacterI18nList from './components/CharacterI18nList.vue'
|
||||
|
||||
/** 键盘人设 列表 */
|
||||
defineOptions({ name: 'KeyboardCharacter' })
|
||||
@@ -180,6 +188,7 @@ const handleDelete = async (id: number) => {
|
||||
// 发起删除
|
||||
await CharacterApi.deleteCharacter(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
currentRow.value = {}
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch { }
|
||||
@@ -202,12 +211,6 @@ const handleRowCheckboxChange = (records: Character[]) => {
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
|
||||
const imagePreview = (imgUrl: string) => {
|
||||
createImageViewer({
|
||||
urlList: [imgUrl]
|
||||
})
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
@@ -223,8 +226,23 @@ const handleExport = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 选中行操作 */
|
||||
const currentRow = ref({}) // 选中行
|
||||
const handleCurrentChange = (row) => {
|
||||
currentRow.value = row
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.character-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
138
src/views/keyboard/characteri18n/CharacterI18nForm.vue
Normal file
138
src/views/keyboard/characteri18n/CharacterI18nForm.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="角色主表id" prop="characterId">
|
||||
<el-input v-model="formData.characterId" placeholder="请输入角色主表id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="语言标识,如 zh-CN/en-US/ja-JP" prop="locale">
|
||||
<el-input v-model="formData.locale" placeholder="请输入语言标识,如 zh-CN/en-US/ja-JP" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标题" prop="characterName">
|
||||
<el-input v-model="formData.characterName" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="背景描述" prop="characterBackground">
|
||||
<el-input v-model="formData.characterBackground" placeholder="请输入背景描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="人设提示词" prop="prompt">
|
||||
<el-input v-model="formData.prompt" placeholder="请输入人设提示词" />
|
||||
</el-form-item>
|
||||
<el-form-item label="emoji 标签" prop="emoji">
|
||||
<el-input v-model="formData.emoji" placeholder="请输入emoji 标签" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker
|
||||
v-model="formData.createdAt"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择创建时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker
|
||||
v-model="formData.updatedAt"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择更新时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { CharacterI18nApi, CharacterI18n } from '@/api/keyboard/characteri18n'
|
||||
|
||||
/** 键盘人设国际化内容 表单 */
|
||||
defineOptions({ name: 'CharacterI18nForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
characterId: undefined,
|
||||
locale: undefined,
|
||||
characterName: undefined,
|
||||
characterBackground: undefined,
|
||||
prompt: undefined,
|
||||
emoji: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
characterId: [{ required: true, message: '角色主表id不能为空', trigger: 'blur' }],
|
||||
locale: [{ required: true, message: '语言标识,如 zh-CN/en-US/ja-JP不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await CharacterI18nApi.getCharacterI18n(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as CharacterI18n
|
||||
if (formType.value === 'create') {
|
||||
await CharacterI18nApi.createCharacterI18n(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await CharacterI18nApi.updateCharacterI18n(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
characterId: undefined,
|
||||
locale: undefined,
|
||||
characterName: undefined,
|
||||
characterBackground: undefined,
|
||||
prompt: undefined,
|
||||
emoji: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
296
src/views/keyboard/characteri18n/index.vue
Normal file
296
src/views/keyboard/characteri18n/index.vue
Normal file
@@ -0,0 +1,296 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="角色主表id" prop="characterId">
|
||||
<el-input
|
||||
v-model="queryParams.characterId"
|
||||
placeholder="请输入角色主表id"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="语言标识,如 zh-CN/en-US/ja-JP" prop="locale">
|
||||
<el-input
|
||||
v-model="queryParams.locale"
|
||||
placeholder="请输入语言标识,如 zh-CN/en-US/ja-JP"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="标题" prop="characterName">
|
||||
<el-input
|
||||
v-model="queryParams.characterName"
|
||||
placeholder="请输入标题"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="背景描述" prop="characterBackground">
|
||||
<el-input
|
||||
v-model="queryParams.characterBackground"
|
||||
placeholder="请输入背景描述"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="人设提示词" prop="prompt">
|
||||
<el-input
|
||||
v-model="queryParams.prompt"
|
||||
placeholder="请输入人设提示词"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="emoji 标签" prop="emoji">
|
||||
<el-input
|
||||
v-model="queryParams.emoji"
|
||||
placeholder="请输入emoji 标签"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createdAt"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="选择创建时间"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker
|
||||
v-model="queryParams.updatedAt"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="选择更新时间"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['keyboard:character-i18n:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['keyboard:character-i18n:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:character-i18n:delete']"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table
|
||||
row-key="id"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="主键 Id" align="center" prop="id" />
|
||||
<el-table-column label="角色主表id" align="center" prop="characterId" />
|
||||
<el-table-column label="语言标识,如 zh-CN/en-US/ja-JP" align="center" prop="locale" />
|
||||
<el-table-column label="标题" align="center" prop="characterName" />
|
||||
<el-table-column label="背景描述" align="center" prop="characterBackground" />
|
||||
<el-table-column label="人设提示词" align="center" prop="prompt" />
|
||||
<el-table-column label="emoji 标签" align="center" prop="emoji" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createdAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="更新时间"
|
||||
align="center"
|
||||
prop="updatedAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard:character-i18n:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard:character-i18n:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CharacterI18nForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { CharacterI18nApi, CharacterI18n } from '@/api/keyboard/characteri18n'
|
||||
import CharacterI18nForm from './CharacterI18nForm.vue'
|
||||
|
||||
/** 键盘人设国际化内容 列表 */
|
||||
defineOptions({ name: 'CharacterI18n' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CharacterI18n[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
characterId: undefined,
|
||||
locale: undefined,
|
||||
characterName: undefined,
|
||||
characterBackground: undefined,
|
||||
prompt: undefined,
|
||||
emoji: undefined,
|
||||
createdAt: undefined,
|
||||
createdAt: [],
|
||||
updatedAt: undefined,
|
||||
updatedAt: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CharacterI18nApi.getCharacterI18nPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await CharacterI18nApi.deleteCharacterI18n(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 批量删除键盘人设国际化内容 */
|
||||
const handleDeleteBatch = async () => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await CharacterI18nApi.deleteCharacterI18nList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList();
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (records: CharacterI18n[]) => {
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await CharacterI18nApi.exportCharacterI18n(queryParams)
|
||||
download.excel(data, '键盘人设国际化内容.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -31,7 +31,7 @@
|
||||
<el-input v-model="formData.currency" placeholder="请输入货币单位" />
|
||||
</el-form-item>
|
||||
<el-form-item label="产品描述" prop="description">
|
||||
<Editor v-model="formData.description" height="150px" />
|
||||
<el-input v-model="formData.description" placeholder="请输入产品描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker v-model="formData.createdAt" type="date" value-format="x" placeholder="选择创建时间" />
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="标签名" prop="tagName">
|
||||
<el-input v-model="formData.tagName" placeholder="请输入标签名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker
|
||||
v-model="formData.createdAt"
|
||||
@@ -48,7 +45,6 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
tagName: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
})
|
||||
@@ -102,7 +98,6 @@ const submitForm = async () => {
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
tagName: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
}
|
||||
|
||||
119
src/views/keyboard/tag/components/TagI18nForm.vue
Normal file
119
src/views/keyboard/tag/components/TagI18nForm.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="语言标识,如 zh-CN、en-US、ja-JP" prop="locale">
|
||||
<el-input v-model="formData.locale" placeholder="请输入语言标识,如 zh-CN、en-US、ja-JP" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标签名称(多语言)" prop="tagName">
|
||||
<el-input v-model="formData.tagName" placeholder="请输入标签名称(多语言)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker
|
||||
v-model="formData.createdAt"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择创建时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker
|
||||
v-model="formData.updatedAt"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择更新时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { TagApi, TagI18n } from '@/api/keyboard/tag'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
tagId: undefined,
|
||||
locale: undefined,
|
||||
tagName: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
tagId: [{ required: true, message: '标签主表ID,对应 keyboard_tag.id不能为空', trigger: 'blur' }],
|
||||
locale: [{ required: true, message: '语言标识,如 zh-CN、en-US、ja-JP不能为空', trigger: 'blur' }],
|
||||
tagName: [{ required: true, message: '标签名称(多语言)不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number, tagId?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
formData.value.tagId = tagId as any
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await TagApi.getTagI18n(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as TagI18n
|
||||
if (formType.value === 'create') {
|
||||
await TagApi.createTagI18n(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await TagApi.updateTagI18n(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
tagId: undefined,
|
||||
locale: undefined,
|
||||
tagName: undefined,
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
170
src/views/keyboard/tag/components/TagI18nList.vue
Normal file
170
src/views/keyboard/tag/components/TagI18nList.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['keyboard:tag:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:tag:delete']"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
<el-table
|
||||
row-key="id"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="主键 Id" align="center" prop="id" />
|
||||
<el-table-column label="语言标识,如 zh-CN、en-US、ja-JP" align="center" prop="locale" />
|
||||
<el-table-column label="标签名称(多语言)" align="center" prop="tagName" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createdAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="更新时间"
|
||||
align="center"
|
||||
prop="updatedAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard:tag:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard:tag:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<TagI18nForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { TagApi, TagI18n } from '@/api/keyboard/tag'
|
||||
import TagI18nForm from './TagI18nForm.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps<{
|
||||
tagId?: number // 标签主表ID,对应 keyboard_tag.id(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
tagId: undefined as unknown
|
||||
})
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.tagId,
|
||||
(val: number) => {
|
||||
if (!val) {
|
||||
list.value = [] // 清空列表
|
||||
return
|
||||
}
|
||||
queryParams.tagId = val
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await TagApi.getTagI18nPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
if (!props.tagId) {
|
||||
message.error('请选择一个人设标签')
|
||||
return
|
||||
}
|
||||
formRef.value.open(type, id, props.tagId)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await TagApi.deleteTagI18n(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 批量删除人设标签国际化 */
|
||||
const handleDeleteBatch = async () => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await TagApi.deleteTagI18nList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList();
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (records: TagI18n[]) => {
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
</script>
|
||||
@@ -1,35 +1,60 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
|
||||
<el-form-item label="标签名" prop="tagName">
|
||||
<el-input v-model="queryParams.tagName" placeholder="请输入标签名" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="创建时间" prop="createdAt">
|
||||
<el-date-picker v-model="queryParams.createdAt" value-format="YYYY-MM-DD" type="date" placeholder="选择创建时间"
|
||||
clearable class="!w-240px" />
|
||||
<el-date-picker
|
||||
v-model="queryParams.createdAt"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="选择创建时间"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker v-model="queryParams.updatedAt" value-format="YYYY-MM-DD" type="date" placeholder="选择更新时间"
|
||||
clearable class="!w-240px" />
|
||||
<el-date-picker
|
||||
v-model="queryParams.updatedAt"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="选择更新时间"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['keyboard:tag:create']">
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['keyboard:tag:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
|
||||
v-hasPermi="['keyboard:tag:export']">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['keyboard:tag:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
<el-button type="danger" plain :disabled="isEmpty(checkedIds)" @click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:tag:delete']">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:tag:delete']"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
@@ -38,31 +63,72 @@
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table row-key="id" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange">
|
||||
<el-table
|
||||
row-key="id"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
highlight-current-row
|
||||
@current-change="handleCurrentChange"
|
||||
@selection-change="handleRowCheckboxChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<!-- <el-table-column label="主键 Id" align="center" prop="id" /> -->
|
||||
<el-table-column label="标签名" align="center" prop="tagName" />
|
||||
<el-table-column label="创建时间" align="center" prop="createdAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="更新时间" align="center" prop="updatedAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="主键 Id" align="center" prop="id" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createdAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="更新时间"
|
||||
align="center"
|
||||
prop="updatedAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['keyboard:tag:update']">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard:tag:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['keyboard:tag:delete']">
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard:tag:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<TagForm ref="formRef" @success="getList" />
|
||||
<!-- 子表的列表 -->
|
||||
<ContentWrap>
|
||||
<el-tabs model-value="tagI18n">
|
||||
<el-tab-pane label="人设标签国际化" name="tagI18n">
|
||||
<TagI18nList :tag-id="currentRow.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -71,6 +137,7 @@ import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { TagApi, Tag } from '@/api/keyboard/tag'
|
||||
import TagForm from './TagForm.vue'
|
||||
import TagI18nList from './components/TagI18nList.vue'
|
||||
|
||||
/** 人设标签 列表 */
|
||||
defineOptions({ name: 'KeyboardTag' })
|
||||
@@ -84,7 +151,6 @@ const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
tagName: undefined,
|
||||
createdAt: undefined,
|
||||
createdAt: [],
|
||||
updatedAt: undefined,
|
||||
@@ -131,6 +197,7 @@ const handleDelete = async (id: number) => {
|
||||
// 发起删除
|
||||
await TagApi.deleteTag(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
currentRow.value = {}
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
@@ -168,6 +235,12 @@ const handleExport = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 选中行操作 */
|
||||
const currentRow = ref({}) // 选中行
|
||||
const handleCurrentChange = (row) => {
|
||||
currentRow.value = row
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
<el-option v-for="item in themeStyleOptions" :key="item.id" :label="item.styleName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属国家" prop="local">
|
||||
<el-input v-model="formData.local" placeholder="请输入所属国家" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上架状态" prop="themeStatus">
|
||||
<el-radio-group v-model="formData.themeStatus">
|
||||
<el-radio v-for="item in themeStatusOptions" :key="item.value" :value="item.value">
|
||||
@@ -96,6 +99,7 @@ const formData = ref({
|
||||
themeTag: [],
|
||||
themeDownload: undefined,
|
||||
themeStyle: undefined,
|
||||
local: undefined,
|
||||
themeStatus: undefined,
|
||||
themePurchasesNumber: undefined,
|
||||
createdAt: undefined,
|
||||
|
||||
@@ -53,6 +53,11 @@
|
||||
<el-input v-model="queryParams.themeDownloadUrl" placeholder="请输入下载地址" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属国家" prop="local">
|
||||
<el-input v-model="queryParams.local" placeholder="请输入所属国家" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input v-model="queryParams.sort" placeholder="请输入排序" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
@@ -121,6 +126,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="主题购买次数" align="center" prop="themePurchasesNumber" />
|
||||
<el-table-column label="所属国家" align="center" prop="local" />
|
||||
<el-table-column label="创建时间" align="center" prop="createdAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="更新时间" align="center" prop="updatedAt" :formatter="dateFormatter" width="180px" />
|
||||
<el-table-column label="预览图" align="center" prop="themePreviewImageUrl" width="120px">
|
||||
@@ -196,6 +202,7 @@ const queryParams = reactive({
|
||||
themePreviewImageUrl: undefined,
|
||||
isFree: undefined,
|
||||
themeDownloadUrl: undefined,
|
||||
local: undefined,
|
||||
sort: undefined,
|
||||
realDownloadCount: undefined
|
||||
})
|
||||
|
||||
116
src/views/keyboard/warningmessage/WarningMessageForm.vue
Normal file
116
src/views/keyboard/warningmessage/WarningMessageForm.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="地区" prop="locale">
|
||||
<el-input v-model="formData.locale" placeholder="请输入地区" />
|
||||
</el-form-item>
|
||||
<el-form-item label="正文" prop="content">
|
||||
<Editor v-model="formData.content" height="150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker
|
||||
v-model="formData.updatedAt"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择更新时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="created">
|
||||
<el-date-picker
|
||||
v-model="formData.created"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择创建时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { WarningMessageApi, WarningMessage } from '@/api/keyboard/warningmessage'
|
||||
|
||||
/** 用户注销提示信息 表单 */
|
||||
defineOptions({ name: 'WarningMessageForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
locale: undefined,
|
||||
content: undefined,
|
||||
updatedAt: undefined,
|
||||
created: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await WarningMessageApi.getWarningMessage(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as WarningMessage
|
||||
if (formType.value === 'create') {
|
||||
await WarningMessageApi.createWarningMessage(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await WarningMessageApi.updateWarningMessage(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
locale: undefined,
|
||||
content: undefined,
|
||||
updatedAt: undefined,
|
||||
created: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
243
src/views/keyboard/warningmessage/index.vue
Normal file
243
src/views/keyboard/warningmessage/index.vue
Normal file
@@ -0,0 +1,243 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="地区" prop="locale">
|
||||
<el-input
|
||||
v-model="queryParams.locale"
|
||||
placeholder="请输入地区"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间" prop="updatedAt">
|
||||
<el-date-picker
|
||||
v-model="queryParams.updatedAt"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="选择更新时间"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="created">
|
||||
<el-date-picker
|
||||
v-model="queryParams.created"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="选择创建时间"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['keyboard:warning-message:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['keyboard:warning-message:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="handleDeleteBatch"
|
||||
v-hasPermi="['keyboard:warning-message:delete']"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table
|
||||
row-key="id"
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="主键" align="center" prop="id" />
|
||||
<el-table-column label="地区" align="center" prop="locale" />
|
||||
<el-table-column label="正文" align="center" prop="content" />
|
||||
<el-table-column
|
||||
label="更新时间"
|
||||
align="center"
|
||||
prop="updatedAt"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="created"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" min-width="120px">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['keyboard:warning-message:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['keyboard:warning-message:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<WarningMessageForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { WarningMessageApi, WarningMessage } from '@/api/keyboard/warningmessage'
|
||||
import WarningMessageForm from './WarningMessageForm.vue'
|
||||
|
||||
/** 用户注销提示信息 列表 */
|
||||
defineOptions({ name: 'KeyboardWarningMessage' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<WarningMessage[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
locale: undefined,
|
||||
content: undefined,
|
||||
updatedAt: undefined,
|
||||
updatedAt: [],
|
||||
created: undefined,
|
||||
created: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await WarningMessageApi.getWarningMessagePage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await WarningMessageApi.deleteWarningMessage(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 批量删除用户注销提示信息 */
|
||||
const handleDeleteBatch = async () => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await WarningMessageApi.deleteWarningMessageList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList();
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (records: WarningMessage[]) => {
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await WarningMessageApi.exportWarningMessage(queryParams)
|
||||
download.excel(data, '用户注销提示信息.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
芋道源码:
|
||||
keyLove:
|
||||
① 移除 avue 组件,使用 ElementUI 原生组件
|
||||
-->
|
||||
<template>
|
||||
@@ -21,12 +21,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分页组件 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getMaterialPageFun"
|
||||
/>
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getMaterialPageFun" />
|
||||
</div>
|
||||
<!-- 类型:voice -->
|
||||
<div v-else-if="props.type === 'voice'">
|
||||
@@ -39,29 +35,18 @@
|
||||
<WxVoicePlayer :url="scope.row.url" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="上传时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180" :formatter="dateFormatter" />
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" link @click="selectMaterialFun(scope.row)"
|
||||
>选择
|
||||
<el-button type="primary" link @click="selectMaterialFun(scope.row)">选择
|
||||
<Icon icon="ep:plus" />
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getPage"
|
||||
/>
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getPage" />
|
||||
</div>
|
||||
<!-- 类型:video -->
|
||||
<div v-else-if="props.type === 'video'">
|
||||
@@ -76,34 +61,18 @@
|
||||
<WxVideoPlayer :url="scope.row.url" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="上传时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
fixed="right"
|
||||
class-name="small-padding fixed-width"
|
||||
>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180" :formatter="dateFormatter" />
|
||||
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" link @click="selectMaterialFun(scope.row)"
|
||||
>选择
|
||||
<el-button type="primary" link @click="selectMaterialFun(scope.row)">选择
|
||||
<Icon icon="akar-icons:circle-plus" />
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getMaterialPageFun"
|
||||
/>
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getMaterialPageFun" />
|
||||
</div>
|
||||
<!-- 类型:news -->
|
||||
<div v-else-if="props.type === 'news'">
|
||||
@@ -121,12 +90,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分页组件 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getMaterialPageFun"
|
||||
/>
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getMaterialPageFun" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
芋道源码:
|
||||
keyLove:
|
||||
① 移除暂时用不到的 websocket
|
||||
② 代码优化,补充注释,提升阅读性
|
||||
-->
|
||||
@@ -11,12 +11,9 @@
|
||||
<!-- 加载更多 -->
|
||||
<div v-loading="loading"></div>
|
||||
<div v-if="!loading">
|
||||
<div class="el-table__empty-block" v-if="hasMore" @click="loadMore"
|
||||
><span class="el-table__empty-text">点击加载更多</span></div
|
||||
>
|
||||
<div class="el-table__empty-block" v-if="!hasMore"
|
||||
><span class="el-table__empty-text">没有更多了</span></div
|
||||
>
|
||||
<div class="el-table__empty-block" v-if="hasMore" @click="loadMore"><span
|
||||
class="el-table__empty-text">点击加载更多</span></div>
|
||||
<div class="el-table__empty-block" v-if="!hasMore"><span class="el-table__empty-text">没有更多了</span></div>
|
||||
</div>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
【微信消息 - 图文】
|
||||
芋道源码:
|
||||
keyLove:
|
||||
① 代码优化,补充注释,提升阅读性
|
||||
-->
|
||||
<template>
|
||||
@@ -12,11 +12,8 @@
|
||||
<a v-if="index === 0" :href="article.url" target="_blank">
|
||||
<div class="news-main">
|
||||
<div class="news-content">
|
||||
<el-image
|
||||
:src="article.picUrl || article.thumbUrl"
|
||||
class="material-img"
|
||||
style="width: 100%; height: 120px"
|
||||
/>
|
||||
<el-image :src="article.picUrl || article.thumbUrl" class="material-img"
|
||||
style="width: 100%; height: 120px" />
|
||||
<div class="news-content-title">
|
||||
<span>{{ article.title }}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
芋道源码:
|
||||
keyLove:
|
||||
① 移除多余的 rep 为前缀的变量,让 message 消息更简单
|
||||
② 代码优化,补充注释,提升阅读性
|
||||
③ 优化消息的临时缓存策略,发送消息时,只清理被发送消息的 tab,不会强制切回到 text 输入
|
||||
@@ -12,7 +12,9 @@
|
||||
<!-- 类型 1:文本 -->
|
||||
<el-tab-pane :name="ReplyType.Text">
|
||||
<template #label>
|
||||
<el-row align="middle"><Icon icon="ep:document" /> 文本</el-row>
|
||||
<el-row align="middle">
|
||||
<Icon icon="ep:document" /> 文本
|
||||
</el-row>
|
||||
</template>
|
||||
<TabText v-model="reply.content" />
|
||||
</el-tab-pane>
|
||||
@@ -20,7 +22,9 @@
|
||||
<!-- 类型 2:图片 -->
|
||||
<el-tab-pane :name="ReplyType.Image">
|
||||
<template #label>
|
||||
<el-row align="middle"><Icon icon="ep:picture" class="mr-5px" /> 图片</el-row>
|
||||
<el-row align="middle">
|
||||
<Icon icon="ep:picture" class="mr-5px" /> 图片
|
||||
</el-row>
|
||||
</template>
|
||||
<TabImage v-model="reply" />
|
||||
</el-tab-pane>
|
||||
@@ -28,7 +32,9 @@
|
||||
<!-- 类型 3:语音 -->
|
||||
<el-tab-pane :name="ReplyType.Voice">
|
||||
<template #label>
|
||||
<el-row align="middle"><Icon icon="ep:phone" /> 语音</el-row>
|
||||
<el-row align="middle">
|
||||
<Icon icon="ep:phone" /> 语音
|
||||
</el-row>
|
||||
</template>
|
||||
<TabVoice v-model="reply" />
|
||||
</el-tab-pane>
|
||||
@@ -36,7 +42,9 @@
|
||||
<!-- 类型 4:视频 -->
|
||||
<el-tab-pane :name="ReplyType.Video">
|
||||
<template #label>
|
||||
<el-row align="middle"><Icon icon="ep:share" /> 视频</el-row>
|
||||
<el-row align="middle">
|
||||
<Icon icon="ep:share" /> 视频
|
||||
</el-row>
|
||||
</template>
|
||||
<TabVideo v-model="reply" />
|
||||
</el-tab-pane>
|
||||
@@ -44,7 +52,9 @@
|
||||
<!-- 类型 5:图文 -->
|
||||
<el-tab-pane :name="ReplyType.News">
|
||||
<template #label>
|
||||
<el-row align="middle"><Icon icon="ep:reading" /> 图文</el-row>
|
||||
<el-row align="middle">
|
||||
<Icon icon="ep:reading" /> 图文
|
||||
</el-row>
|
||||
</template>
|
||||
<TabNews v-model="reply" :news-type="newsType" />
|
||||
</el-tab-pane>
|
||||
@@ -52,7 +62,9 @@
|
||||
<!-- 类型 6:音乐 -->
|
||||
<el-tab-pane :name="ReplyType.Music">
|
||||
<template #label>
|
||||
<el-row align="middle"><Icon icon="ep:service" />音乐</el-row>
|
||||
<el-row align="middle">
|
||||
<Icon icon="ep:service" />音乐
|
||||
</el-row>
|
||||
</template>
|
||||
<TabMusic v-model="reply" />
|
||||
</el-tab-pane>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
【微信消息 - 视频】
|
||||
芋道源码:
|
||||
keyLove:
|
||||
① bug 修复:
|
||||
1)joolun 的做法:使用 mediaId 从微信公众号,下载对应的 mp4 素材,从而播放内容;
|
||||
存在的问题:mediaId 有效期是 3 天,超过时间后无法播放
|
||||
@@ -20,18 +20,9 @@
|
||||
|
||||
<!-- 弹窗播放 -->
|
||||
<el-dialog v-model="dialogVideo" title="视频播放" append-to-body>
|
||||
<video-player
|
||||
v-if="dialogVideo"
|
||||
class="video-player vjs-big-play-centered"
|
||||
:src="props.url"
|
||||
poster=""
|
||||
crossorigin="anonymous"
|
||||
controls
|
||||
playsinline
|
||||
:volume="0.6"
|
||||
:width="800"
|
||||
:playback-rates="[0.7, 1.0, 1.5, 2.0]"
|
||||
/>
|
||||
<video-player v-if="dialogVideo" class="video-player vjs-big-play-centered" :src="props.url" poster=""
|
||||
crossorigin="anonymous" controls playsinline :volume="0.6" :width="800"
|
||||
:playback-rates="[0.7, 1.0, 1.5, 2.0]" />
|
||||
<!-- 事件,暫時沒用
|
||||
@mounted="handleMounted"-->
|
||||
<!-- @ready="handleEvent($event)"-->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
- Copyright (C) 2018-2019
|
||||
- All rights reserved, Designed By www.joolun.com
|
||||
【微信消息 - 语音】
|
||||
芋道源码:
|
||||
keyLove:
|
||||
① bug 修复:
|
||||
1)joolun 的做法:使用 mediaId 从微信公众号,下载对应的 mp4 素材,从而播放内容;
|
||||
存在的问题:mediaId 有效期是 3 天,超过时间后无法播放
|
||||
|
||||
@@ -6,12 +6,7 @@
|
||||
<div class="item-name">{{ item.name }}</div>
|
||||
</a>
|
||||
<el-row justify="center">
|
||||
<el-button
|
||||
type="danger"
|
||||
circle
|
||||
@click="emit('delete', item.id)"
|
||||
v-hasPermi="['mp:material:delete']"
|
||||
>
|
||||
<el-button type="danger" circle @click="emit('delete', item.id)" v-hasPermi="['mp:material:delete']">
|
||||
<Icon icon="ep:delete" />
|
||||
</el-button>
|
||||
</el-row>
|
||||
@@ -63,7 +58,7 @@ const emit = defineEmits<{
|
||||
column-count: 5;
|
||||
margin-top: 10px;
|
||||
|
||||
/* 芋道源码:增加 10px,避免顶着上面 */
|
||||
/* keyLove:增加 10px,避免顶着上面 */
|
||||
}
|
||||
|
||||
.waterfall-item {
|
||||
|
||||
Reference in New Issue
Block a user