style: 使用 Prettier 格式化源码
parent
3208a76868
commit
18ed1cdfed
|
|
@ -39,4 +39,4 @@ export const ProcessExpressionApi = {
|
||||||
exportProcessExpression: async (params) => {
|
exportProcessExpression: async (params) => {
|
||||||
return await request.download({ url: `/bpm/process-expression/export-excel`, params })
|
return await request.download({ url: `/bpm/process-expression/export-excel`, params })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
|
||||||
export const updateBpmSimpleModel = async (data) => {
|
export const updateBpmSimpleModel = async (data) => {
|
||||||
return await request.post({
|
return await request.post({
|
||||||
url: '/bpm/model/simple/update',
|
url: '/bpm/model/simple/update',
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,11 @@ export const getMyFriendList = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 增量拉取当前用户的好友关系(重连 / 离线补偿)
|
// 增量拉取当前用户的好友关系(重连 / 离线补偿)
|
||||||
export const pullMyFriendList = (params: { lastUpdateTime?: number; lastId?: number; limit: number }) => {
|
export const pullMyFriendList = (params: {
|
||||||
|
lastUpdateTime?: number
|
||||||
|
lastId?: number
|
||||||
|
limit: number
|
||||||
|
}) => {
|
||||||
return request.get<ImFriendRespVO[]>({ url: '/im/friend/pull', params })
|
return request.get<ImFriendRespVO[]>({ url: '/im/friend/pull', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,4 +69,3 @@ export const blockFriend = (friendUserId: number | string) => {
|
||||||
export const unblockFriend = (friendUserId: number | string) => {
|
export const unblockFriend = (friendUserId: number | string) => {
|
||||||
return request.put<boolean>({ url: '/im/friend/unblock', params: { friendUserId } })
|
return request.put<boolean>({ url: '/im/friend/unblock', params: { friendUserId } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,10 @@ export const getMyGroupRequest = (id: number) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 增量拉取我管理的所有群下加群申请变更(重连 / 离线补偿)
|
// 增量拉取我管理的所有群下加群申请变更(重连 / 离线补偿)
|
||||||
export const pullMyGroupRequestList = (params: { lastUpdateTime?: number; lastId?: number; limit: number }) => {
|
export const pullMyGroupRequestList = (params: {
|
||||||
|
lastUpdateTime?: number
|
||||||
|
lastId?: number
|
||||||
|
limit: number
|
||||||
|
}) => {
|
||||||
return request.get<ImGroupRequestRespVO[]>({ url: '/im/group-request/pull', params })
|
return request.get<ImGroupRequestRespVO[]>({ url: '/im/group-request/pull', params })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,18 @@ export const getStatisticsOverview = (): Promise<ImStatisticsOverviewVO> => {
|
||||||
|
|
||||||
// 获得消息趋势(私聊 + 群聊双线)
|
// 获得消息趋势(私聊 + 群聊双线)
|
||||||
export const getMessageTrend = (days: number): Promise<ImStatisticsTrendVO> => {
|
export const getMessageTrend = (days: number): Promise<ImStatisticsTrendVO> => {
|
||||||
return request.get<ImStatisticsTrendVO>({ url: '/im/manager/statistics/message-trend', params: { days } })
|
return request.get<ImStatisticsTrendVO>({
|
||||||
|
url: '/im/manager/statistics/message-trend',
|
||||||
|
params: { days }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得用户趋势(新增注册 + 日活双线)
|
// 获得用户趋势(新增注册 + 日活双线)
|
||||||
export const getUserTrend = (days: number): Promise<ImStatisticsTrendVO> => {
|
export const getUserTrend = (days: number): Promise<ImStatisticsTrendVO> => {
|
||||||
return request.get<ImStatisticsTrendVO>({ url: '/im/manager/statistics/user-trend', params: { days } })
|
return request.get<ImStatisticsTrendVO>({
|
||||||
|
url: '/im/manager/statistics/user-trend',
|
||||||
|
params: { days }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得内容类型分布(最近 30 天)
|
// 获得内容类型分布(最近 30 天)
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,10 @@ export const deleteDataSourceConfig = (id: number) => {
|
||||||
|
|
||||||
// 批量删除数据源配置
|
// 批量删除数据源配置
|
||||||
export const deleteDataSourceConfigList = (ids: number[]) => {
|
export const deleteDataSourceConfigList = (ids: number[]) => {
|
||||||
return request.delete({ url: '/infra/data-source-config/delete-list', params: { ids: ids.join(',') } })
|
return request.delete({
|
||||||
|
url: '/infra/data-source-config/delete-list',
|
||||||
|
params: { ids: ids.join(',') }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询数据源配置详情
|
// 查询数据源配置详情
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs'
|
||||||
|
|
||||||
/** 学生课程信息 */
|
/** 学生课程信息 */
|
||||||
export interface Demo03Course {
|
export interface Demo03Course {
|
||||||
id: number; // 编号
|
id: number // 编号
|
||||||
studentId?: number; // 学生编号
|
studentId?: number // 学生编号
|
||||||
name?: string; // 名字
|
name?: string // 名字
|
||||||
score?: number; // 分数
|
score?: number // 分数
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 学生班级信息 */
|
/** 学生班级信息 */
|
||||||
export interface Demo03Grade {
|
export interface Demo03Grade {
|
||||||
id: number; // 编号
|
id: number // 编号
|
||||||
studentId?: number; // 学生编号
|
studentId?: number // 学生编号
|
||||||
name?: string; // 名字
|
name?: string // 名字
|
||||||
teacher?: string; // 班主任
|
teacher?: string // 班主任
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 学生信息 */
|
/** 学生信息 */
|
||||||
export interface Demo03Student {
|
export interface Demo03Student {
|
||||||
id: number; // 编号
|
id: number // 编号
|
||||||
name?: string; // 名字
|
name?: string // 名字
|
||||||
sex?: number; // 性别
|
sex?: number // 性别
|
||||||
birthday?: string | Dayjs; // 出生日期
|
birthday?: string | Dayjs // 出生日期
|
||||||
description?: string; // 简介
|
description?: string // 简介
|
||||||
}
|
}
|
||||||
|
|
||||||
// 学生 API
|
// 学生 API
|
||||||
|
|
@ -55,7 +55,9 @@ export const Demo03StudentApi = {
|
||||||
|
|
||||||
/** 批量删除学生 */
|
/** 批量删除学生 */
|
||||||
deleteDemo03StudentList: async (ids: number[]) => {
|
deleteDemo03StudentList: async (ids: number[]) => {
|
||||||
return await request.delete({ url: `/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}` })
|
return await request.delete({
|
||||||
|
url: `/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}`
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出学生 Excel
|
// 导出学生 Excel
|
||||||
|
|
@ -63,7 +65,7 @@ export const Demo03StudentApi = {
|
||||||
return await request.download({ url: `/infra/demo03-student-erp/export-excel`, params })
|
return await request.download({ url: `/infra/demo03-student-erp/export-excel`, params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// ==================== 子表(学生课程) ====================
|
// ==================== 子表(学生课程) ====================
|
||||||
|
|
||||||
// 获得学生课程分页
|
// 获得学生课程分页
|
||||||
getDemo03CoursePage: async (params) => {
|
getDemo03CoursePage: async (params) => {
|
||||||
|
|
@ -86,7 +88,9 @@ export const Demo03StudentApi = {
|
||||||
|
|
||||||
/** 批量删除学生课程 */
|
/** 批量删除学生课程 */
|
||||||
deleteDemo03CourseList: async (ids: number[]) => {
|
deleteDemo03CourseList: async (ids: number[]) => {
|
||||||
return await request.delete({ url: `/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}` })
|
return await request.delete({
|
||||||
|
url: `/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}`
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获得学生课程
|
// 获得学生课程
|
||||||
|
|
@ -94,7 +98,7 @@ export const Demo03StudentApi = {
|
||||||
return await request.get({ url: `/infra/demo03-student-erp/demo03-course/get?id=` + id })
|
return await request.get({ url: `/infra/demo03-student-erp/demo03-course/get?id=` + id })
|
||||||
},
|
},
|
||||||
|
|
||||||
// ==================== 子表(学生班级) ====================
|
// ==================== 子表(学生班级) ====================
|
||||||
|
|
||||||
// 获得学生班级分页
|
// 获得学生班级分页
|
||||||
getDemo03GradePage: async (params) => {
|
getDemo03GradePage: async (params) => {
|
||||||
|
|
@ -117,11 +121,13 @@ export const Demo03StudentApi = {
|
||||||
|
|
||||||
/** 批量删除学生班级 */
|
/** 批量删除学生班级 */
|
||||||
deleteDemo03GradeList: async (ids: number[]) => {
|
deleteDemo03GradeList: async (ids: number[]) => {
|
||||||
return await request.delete({ url: `/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}` })
|
return await request.delete({
|
||||||
|
url: `/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}`
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获得学生班级
|
// 获得学生班级
|
||||||
getDemo03Grade: async (id: number) => {
|
getDemo03Grade: async (id: number) => {
|
||||||
return await request.get({ url: `/infra/demo03-student-erp/demo03-grade/get?id=` + id })
|
return await request.get({ url: `/infra/demo03-student-erp/demo03-grade/get?id=` + id })
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs'
|
||||||
|
|
||||||
/** 学生课程信息 */
|
/** 学生课程信息 */
|
||||||
export interface Demo03Course {
|
export interface Demo03Course {
|
||||||
id: number; // 编号
|
id: number // 编号
|
||||||
studentId?: number; // 学生编号
|
studentId?: number // 学生编号
|
||||||
name?: string; // 名字
|
name?: string // 名字
|
||||||
score?: number; // 分数
|
score?: number // 分数
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 学生班级信息 */
|
/** 学生班级信息 */
|
||||||
export interface Demo03Grade {
|
export interface Demo03Grade {
|
||||||
id: number; // 编号
|
id: number // 编号
|
||||||
studentId?: number; // 学生编号
|
studentId?: number // 学生编号
|
||||||
name?: string; // 名字
|
name?: string // 名字
|
||||||
teacher?: string; // 班主任
|
teacher?: string // 班主任
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 学生信息 */
|
/** 学生信息 */
|
||||||
export interface Demo03Student {
|
export interface Demo03Student {
|
||||||
id: number; // 编号
|
id: number // 编号
|
||||||
name?: string; // 名字
|
name?: string // 名字
|
||||||
sex?: number; // 性别
|
sex?: number // 性别
|
||||||
birthday?: string | Dayjs; // 出生日期
|
birthday?: string | Dayjs // 出生日期
|
||||||
description?: string; // 简介
|
description?: string // 简介
|
||||||
demo03courses?: Demo03Course[]
|
demo03courses?: Demo03Course[]
|
||||||
demo03grade?: Demo03Grade
|
demo03grade?: Demo03Grade
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,9 @@ export const Demo03StudentApi = {
|
||||||
|
|
||||||
/** 批量删除学生 */
|
/** 批量删除学生 */
|
||||||
deleteDemo03StudentList: async (ids: number[]) => {
|
deleteDemo03StudentList: async (ids: number[]) => {
|
||||||
return await request.delete({ url: `/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}` })
|
return await request.delete({
|
||||||
|
url: `/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}`
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出学生 Excel
|
// 导出学生 Excel
|
||||||
|
|
@ -65,17 +67,21 @@ export const Demo03StudentApi = {
|
||||||
return await request.download({ url: `/infra/demo03-student-inner/export-excel`, params })
|
return await request.download({ url: `/infra/demo03-student-inner/export-excel`, params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// ==================== 子表(学生课程) ====================
|
// ==================== 子表(学生课程) ====================
|
||||||
|
|
||||||
// 获得学生课程列表
|
// 获得学生课程列表
|
||||||
getDemo03CourseListByStudentId: async (studentId) => {
|
getDemo03CourseListByStudentId: async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student-inner/demo03-course/list-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
|
url: `/infra/demo03-student-inner/demo03-course/list-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// ==================== 子表(学生班级) ====================
|
// ==================== 子表(学生班级) ====================
|
||||||
|
|
||||||
// 获得学生班级
|
// 获得学生班级
|
||||||
getDemo03GradeByStudentId: async (studentId) => {
|
getDemo03GradeByStudentId: async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student-inner/demo03-grade/get-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
},
|
url: `/infra/demo03-student-inner/demo03-grade/get-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs'
|
||||||
|
|
||||||
/** 学生课程信息 */
|
/** 学生课程信息 */
|
||||||
export interface Demo03Course {
|
export interface Demo03Course {
|
||||||
id: number; // 编号
|
id: number // 编号
|
||||||
studentId?: number; // 学生编号
|
studentId?: number // 学生编号
|
||||||
name?: string; // 名字
|
name?: string // 名字
|
||||||
score?: number; // 分数
|
score?: number // 分数
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 学生班级信息 */
|
/** 学生班级信息 */
|
||||||
export interface Demo03Grade {
|
export interface Demo03Grade {
|
||||||
id: number; // 编号
|
id: number // 编号
|
||||||
studentId?: number; // 学生编号
|
studentId?: number // 学生编号
|
||||||
name?: string; // 名字
|
name?: string // 名字
|
||||||
teacher?: string; // 班主任
|
teacher?: string // 班主任
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 学生信息 */
|
/** 学生信息 */
|
||||||
export interface Demo03Student {
|
export interface Demo03Student {
|
||||||
id: number; // 编号
|
id: number // 编号
|
||||||
name?: string; // 名字
|
name?: string // 名字
|
||||||
sex?: number; // 性别
|
sex?: number // 性别
|
||||||
birthday?: string | Dayjs; // 出生日期
|
birthday?: string | Dayjs // 出生日期
|
||||||
description?: string; // 简介
|
description?: string // 简介
|
||||||
demo03courses?: Demo03Course[]
|
demo03courses?: Demo03Course[]
|
||||||
demo03grade?: Demo03Grade
|
demo03grade?: Demo03Grade
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,9 @@ export const Demo03StudentApi = {
|
||||||
|
|
||||||
/** 批量删除学生 */
|
/** 批量删除学生 */
|
||||||
deleteDemo03StudentList: async (ids: number[]) => {
|
deleteDemo03StudentList: async (ids: number[]) => {
|
||||||
return await request.delete({ url: `/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}` })
|
return await request.delete({
|
||||||
|
url: `/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}`
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// 导出学生 Excel
|
// 导出学生 Excel
|
||||||
|
|
@ -65,17 +67,21 @@ export const Demo03StudentApi = {
|
||||||
return await request.download({ url: `/infra/demo03-student-normal/export-excel`, params })
|
return await request.download({ url: `/infra/demo03-student-normal/export-excel`, params })
|
||||||
},
|
},
|
||||||
|
|
||||||
// ==================== 子表(学生课程) ====================
|
// ==================== 子表(学生课程) ====================
|
||||||
|
|
||||||
// 获得学生课程列表
|
// 获得学生课程列表
|
||||||
getDemo03CourseListByStudentId: async (studentId) => {
|
getDemo03CourseListByStudentId: async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student-normal/demo03-course/list-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
|
url: `/infra/demo03-student-normal/demo03-course/list-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// ==================== 子表(学生班级) ====================
|
// ==================== 子表(学生班级) ====================
|
||||||
|
|
||||||
// 获得学生班级
|
// 获得学生班级
|
||||||
getDemo03GradeByStudentId: async (studentId) => {
|
getDemo03GradeByStudentId: async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student-normal/demo03-grade/get-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
},
|
url: `/infra/demo03-student-normal/demo03-grade/get-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,10 @@ export const deleteMailAccount = async (id: number) => {
|
||||||
|
|
||||||
// 批量删除邮箱账号
|
// 批量删除邮箱账号
|
||||||
export const deleteMailAccountList = async (ids: number[]) => {
|
export const deleteMailAccountList = async (ids: number[]) => {
|
||||||
return await request.delete({ url: '/system/mail-account/delete-list', params: { ids: ids.join(',') } })
|
return await request.delete({
|
||||||
|
url: '/system/mail-account/delete-list',
|
||||||
|
params: { ids: ids.join(',') }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得邮箱账号精简列表
|
// 获得邮箱账号精简列表
|
||||||
|
|
|
||||||
|
|
@ -48,5 +48,8 @@ export const deleteOAuth2Client = (id: number) => {
|
||||||
|
|
||||||
// 批量删除 OAuth2 客户端
|
// 批量删除 OAuth2 客户端
|
||||||
export const deleteOAuth2ClientList = (ids: number[]) => {
|
export const deleteOAuth2ClientList = (ids: number[]) => {
|
||||||
return request.delete({ url: '/system/oauth2-client/delete-list', params: { ids: ids.join(',') } })
|
return request.delete({
|
||||||
|
url: '/system/oauth2-client/delete-list',
|
||||||
|
params: { ids: ids.join(',') }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,10 @@ export const deleteTenantPackage = (id: number) => {
|
||||||
|
|
||||||
// 批量删除租户套餐
|
// 批量删除租户套餐
|
||||||
export const deleteTenantPackageList = (ids: number[]) => {
|
export const deleteTenantPackageList = (ids: number[]) => {
|
||||||
return request.delete({ url: '/system/tenant-package/delete-list', params: { ids: ids.join(',') } })
|
return request.delete({
|
||||||
|
url: '/system/tenant-package/delete-list',
|
||||||
|
params: { ids: ids.join(',') }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取租户套餐精简信息列表
|
// 获取租户套餐精简信息列表
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,12 @@
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="高度" prop="height">
|
<el-form-item label="高度" prop="height">
|
||||||
<el-input-number class="!w-50% mr-10px" controls-position="right" v-model="formData.height" /> px
|
<el-input-number
|
||||||
|
class="!w-50% mr-10px"
|
||||||
|
controls-position="right"
|
||||||
|
v-model="formData.height"
|
||||||
|
/>
|
||||||
|
px
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="指示器" prop="indicator">
|
<el-form-item label="指示器" prop="indicator">
|
||||||
<el-radio-group v-model="formData.indicator">
|
<el-radio-group v-model="formData.indicator">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import {ComponentStyle, DiyComponent} from '@/components/DiyEditor/util'
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
/** 积分商城属性 */
|
/** 积分商城属性 */
|
||||||
export interface PromotionPointProperty {
|
export interface PromotionPointProperty {
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,7 @@
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="高度" prop="height" label-width="70px">
|
<el-form-item label="高度" prop="height" label-width="70px">
|
||||||
<el-slider
|
<el-slider v-model="formData.height" :max="200" :min="20" show-input input-size="small" />
|
||||||
v-model="formData.height"
|
|
||||||
:max="200"
|
|
||||||
:min="20"
|
|
||||||
show-input
|
|
||||||
input-size="small"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card header="主标题" class="property-group" shadow="never">
|
<el-card header="主标题" class="property-group" shadow="never">
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ interface AreaVO {
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue?: number[] | string[]
|
modelValue?: number[] | string[]
|
||||||
level?: typeof AreaLevelEnum[keyof typeof AreaLevelEnum]
|
level?: (typeof AreaLevelEnum)[keyof typeof AreaLevelEnum]
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
clearable?: boolean
|
clearable?: boolean
|
||||||
|
|
|
||||||
|
|
@ -155,32 +155,32 @@ const hasValidPresetValue = (): boolean => {
|
||||||
// 设置默认值(当前用户部门)
|
// 设置默认值(当前用户部门)
|
||||||
const setDefaultValue = () => {
|
const setDefaultValue = () => {
|
||||||
console.log('[DeptSelect] setDefaultValue called, defaultCurrentDept:', props.defaultCurrentDept)
|
console.log('[DeptSelect] setDefaultValue called, defaultCurrentDept:', props.defaultCurrentDept)
|
||||||
|
|
||||||
// 仅当 defaultCurrentDept 为 true 时处理
|
// 仅当 defaultCurrentDept 为 true 时处理
|
||||||
if (!props.defaultCurrentDept) {
|
if (!props.defaultCurrentDept) {
|
||||||
console.log('[DeptSelect] defaultCurrentDept is false, skip')
|
console.log('[DeptSelect] defaultCurrentDept is false, skip')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否已有预设值(预设值优先级高于默认当前部门)
|
// 检查是否已有预设值(预设值优先级高于默认当前部门)
|
||||||
if (hasValidPresetValue()) {
|
if (hasValidPresetValue()) {
|
||||||
console.log('[DeptSelect] has preset value, skip:', props.modelValue)
|
console.log('[DeptSelect] has preset value, skip:', props.modelValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前用户的部门 ID
|
// 获取当前用户的部门 ID
|
||||||
const userStore = useUserStoreWithOut()
|
const userStore = useUserStoreWithOut()
|
||||||
const user = userStore.getUser
|
const user = userStore.getUser
|
||||||
const deptId = user?.deptId
|
const deptId = user?.deptId
|
||||||
|
|
||||||
console.log('[DeptSelect] current user:', user, 'deptId:', deptId)
|
console.log('[DeptSelect] current user:', user, 'deptId:', deptId)
|
||||||
|
|
||||||
// 处理 deptId 为空或 0 的边界情况
|
// 处理 deptId 为空或 0 的边界情况
|
||||||
if (!deptId || deptId === 0) {
|
if (!deptId || deptId === 0) {
|
||||||
console.log('[DeptSelect] deptId is invalid, skip')
|
console.log('[DeptSelect] deptId is invalid, skip')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据多选模式决定默认值格式
|
// 根据多选模式决定默认值格式
|
||||||
const defaultValue = props.multiple ? [deptId] : deptId
|
const defaultValue = props.multiple ? [deptId] : deptId
|
||||||
console.log('[DeptSelect] setting default value:', defaultValue)
|
console.log('[DeptSelect] setting default value:', defaultValue)
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,14 @@ export const useFormCreateDesigner = async (designer: Ref) => {
|
||||||
designer.value?.removeMenuItem('fcEditor')
|
designer.value?.removeMenuItem('fcEditor')
|
||||||
const iframeRule = useIframeRule()
|
const iframeRule = useIframeRule()
|
||||||
const areaSelectRule = useAreaSelectRule()
|
const areaSelectRule = useAreaSelectRule()
|
||||||
const components = [editorRule, uploadFileRule, uploadImgRule, uploadImgsRule, iframeRule, areaSelectRule]
|
const components = [
|
||||||
|
editorRule,
|
||||||
|
uploadFileRule,
|
||||||
|
uploadImgRule,
|
||||||
|
uploadImgsRule,
|
||||||
|
iframeRule,
|
||||||
|
areaSelectRule
|
||||||
|
]
|
||||||
components.forEach((component) => {
|
components.forEach((component) => {
|
||||||
// 插入组件规则
|
// 插入组件规则
|
||||||
designer.value?.addComponent(component)
|
designer.value?.addComponent(component)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const localeProps = (t, prefix, rules) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析表单组件的 field, title 等字段(递归,如果组件包含子组件)
|
* 解析表单组件的 field, title 等字段(递归,如果组件包含子组件)
|
||||||
*
|
*
|
||||||
* @param rule 组件的生成规则 https://www.form-create.com/v3/guide/rule
|
* @param rule 组件的生成规则 https://www.form-create.com/v3/guide/rule
|
||||||
* @param fields 解析后表单组件字段
|
* @param fields 解析后表单组件字段
|
||||||
* @param parentTitle 如果是子表单,子表单的标题,默认为空
|
* @param parentTitle 如果是子表单,子表单的标题,默认为空
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,12 @@ watch(
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="selector">
|
<div class="selector">
|
||||||
<ElInput v-model="inputValue" @click="visible = !visible" :clearable="props.clearable" @clear="clearIcon">
|
<ElInput
|
||||||
|
v-model="inputValue"
|
||||||
|
@click="visible = !visible"
|
||||||
|
:clearable="props.clearable"
|
||||||
|
@clear="clearIcon"
|
||||||
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<ElPopover
|
<ElPopover
|
||||||
:popper-options="{
|
:popper-options="{
|
||||||
|
|
|
||||||
|
|
@ -5,43 +5,43 @@ export interface JsonEditorProps {
|
||||||
* JSON数据,支持双向绑定
|
* JSON数据,支持双向绑定
|
||||||
*/
|
*/
|
||||||
modelValue: any
|
modelValue: any
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编辑器模式
|
* 编辑器模式
|
||||||
* @default 'tree'
|
* @default 'tree'
|
||||||
*/
|
*/
|
||||||
mode?: JSONEditorMode
|
mode?: JSONEditorMode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编辑器高度
|
* 编辑器高度
|
||||||
* @default '400px'
|
* @default '400px'
|
||||||
*/
|
*/
|
||||||
height?: string
|
height?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示模式选择下拉菜单
|
* 是否显示模式选择下拉菜单
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
showModeSelection?: boolean
|
showModeSelection?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示导航栏
|
* 是否显示导航栏
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
showNavigationBar?: boolean
|
showNavigationBar?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示状态栏
|
* 是否显示状态栏
|
||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
showStatusBar?: boolean
|
showStatusBar?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示主菜单栏
|
* 是否显示主菜单栏
|
||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
showMainMenuBar?: boolean
|
showMainMenuBar?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSONEditor配置选项
|
* JSONEditor配置选项
|
||||||
* @see https://github.com/josdejong/jsoneditor/blob/develop/docs/api.md
|
* @see https://github.com/josdejong/jsoneditor/blob/develop/docs/api.md
|
||||||
|
|
@ -57,12 +57,12 @@ export interface JsonEditorEmits {
|
||||||
* 数据更新时触发
|
* 数据更新时触发
|
||||||
*/
|
*/
|
||||||
(e: 'update:modelValue', value: any): void
|
(e: 'update:modelValue', value: any): void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据变化时触发
|
* 数据变化时触发
|
||||||
*/
|
*/
|
||||||
(e: 'change', value: any): void
|
(e: 'change', value: any): void
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证错误时触发
|
* 验证错误时触发
|
||||||
*/
|
*/
|
||||||
|
|
@ -77,4 +77,4 @@ export interface JsonEditorExpose {
|
||||||
* 获取原始的JSONEditor实例
|
* 获取原始的JSONEditor实例
|
||||||
*/
|
*/
|
||||||
getEditor: () => any
|
getEditor: () => any
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,13 +50,13 @@ const props = defineProps({
|
||||||
modelFormId: {
|
modelFormId: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: undefined,
|
default: undefined
|
||||||
},
|
},
|
||||||
// 表单类型
|
// 表单类型
|
||||||
modelFormType: {
|
modelFormType: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: BpmModelFormType.NORMAL,
|
default: BpmModelFormType.NORMAL
|
||||||
},
|
},
|
||||||
// 可发起流程的人员编号
|
// 可发起流程的人员编号
|
||||||
startUserIds: {
|
startUserIds: {
|
||||||
|
|
@ -73,30 +73,30 @@ const props = defineProps({
|
||||||
const processData = inject('processData') as Ref
|
const processData = inject('processData') as Ref
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const formFields = ref<string[]>([])
|
const formFields = ref<string[]>([])
|
||||||
const formType = ref(props.modelFormType);
|
const formType = ref(props.modelFormType)
|
||||||
|
|
||||||
// 监听 modelFormType 变化
|
// 监听 modelFormType 变化
|
||||||
watch(
|
watch(
|
||||||
() => props.modelFormType,
|
() => props.modelFormType,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
formType.value = newVal;
|
formType.value = newVal
|
||||||
},
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
// 监听 modelFormId 变化
|
// 监听 modelFormId 变化
|
||||||
watch(
|
watch(
|
||||||
() => props.modelFormId,
|
() => props.modelFormId,
|
||||||
async (newVal) => {
|
async (newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
const form = await getForm(newVal);
|
const form = await getForm(newVal)
|
||||||
formFields.value = form?.fields;
|
formFields.value = form?.fields
|
||||||
} else {
|
} else {
|
||||||
// 如果 modelFormId 为空,清空表单字段
|
// 如果 modelFormId 为空,清空表单字段
|
||||||
formFields.value = [];
|
formFields.value = []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true }
|
||||||
);
|
)
|
||||||
|
|
||||||
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
|
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
|
||||||
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
|
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
|
||||||
|
|
@ -118,7 +118,6 @@ provide('startDeptIds', props.startDeptIds)
|
||||||
provide('tasks', [])
|
provide('tasks', [])
|
||||||
provide('processInstance', {})
|
provide('processInstance', {})
|
||||||
|
|
||||||
|
|
||||||
const message = useMessage() // 国际化
|
const message = useMessage() // 国际化
|
||||||
const processNodeTree = ref<SimpleFlowNode | undefined>()
|
const processNodeTree = ref<SimpleFlowNode | undefined>()
|
||||||
provide('processNodeTree', processNodeTree)
|
provide('processNodeTree', processNodeTree)
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,4 @@ import SimpleProcessDesigner from './SimpleProcessDesigner.vue'
|
||||||
import SimpleProcessViewer from './SimpleProcessViewer.vue'
|
import SimpleProcessViewer from './SimpleProcessViewer.vue'
|
||||||
import '../theme/simple-process-designer.scss'
|
import '../theme/simple-process-designer.scss'
|
||||||
|
|
||||||
export { SimpleProcessDesigner, SimpleProcessViewer}
|
export { SimpleProcessDesigner, SimpleProcessViewer }
|
||||||
|
|
|
||||||
|
|
@ -295,10 +295,20 @@
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="configForm.multiInstanceSourceType === ChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY">
|
<el-form-item
|
||||||
|
v-if="
|
||||||
|
configForm.multiInstanceSourceType ===
|
||||||
|
ChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY
|
||||||
|
"
|
||||||
|
>
|
||||||
<el-input-number v-model="configForm.multiInstanceSource" :min="1" />
|
<el-input-number v-model="configForm.multiInstanceSource" :min="1" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="configForm.multiInstanceSourceType === ChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM">
|
<el-form-item
|
||||||
|
v-if="
|
||||||
|
configForm.multiInstanceSourceType ===
|
||||||
|
ChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM
|
||||||
|
"
|
||||||
|
>
|
||||||
<el-select class="w-200px!" v-model="configForm.multiInstanceSource">
|
<el-select class="w-200px!" v-model="configForm.multiInstanceSource">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(field, fIdx) in digitalFormFieldOptions"
|
v-for="(field, fIdx) in digitalFormFieldOptions"
|
||||||
|
|
@ -308,7 +318,12 @@
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="configForm.multiInstanceSourceType === ChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM">
|
<el-form-item
|
||||||
|
v-if="
|
||||||
|
configForm.multiInstanceSourceType ===
|
||||||
|
ChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM
|
||||||
|
"
|
||||||
|
>
|
||||||
<el-select class="w-200px!" v-model="configForm.multiInstanceSource">
|
<el-select class="w-200px!" v-model="configForm.multiInstanceSource">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="(field, fIdx) in multiFormFieldOptions"
|
v-for="(field, fIdx) in multiFormFieldOptions"
|
||||||
|
|
@ -519,7 +534,9 @@ const showChildProcessNodeConfig = (node: SimpleFlowNode) => {
|
||||||
configForm.value.outVariables = node.childProcessSetting.outVariables
|
configForm.value.outVariables = node.childProcessSetting.outVariables
|
||||||
// 6. 发起人设置
|
// 6. 发起人设置
|
||||||
configForm.value.startUserType = node.childProcessSetting.startUserSetting.type
|
configForm.value.startUserType = node.childProcessSetting.startUserSetting.type
|
||||||
configForm.value.startUserEmptyType = node.childProcessSetting.startUserSetting.emptyType ?? ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER
|
configForm.value.startUserEmptyType =
|
||||||
|
node.childProcessSetting.startUserSetting.emptyType ??
|
||||||
|
ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER
|
||||||
configForm.value.startUserFormField = node.childProcessSetting.startUserSetting.formField ?? ''
|
configForm.value.startUserFormField = node.childProcessSetting.startUserSetting.formField ?? ''
|
||||||
// 7. 超时设置
|
// 7. 超时设置
|
||||||
configForm.value.timeoutEnable = node.childProcessSetting.timeoutSetting.enable ?? false
|
configForm.value.timeoutEnable = node.childProcessSetting.timeoutSetting.enable ?? false
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
import { TimeUnitType, ApproveType, APPROVE_TYPE } from './consts'
|
import { TimeUnitType, ApproveType, APPROVE_TYPE } from './consts'
|
||||||
|
|
||||||
// 获取条件节点默认的名称
|
// 获取条件节点默认的名称
|
||||||
export const getDefaultConditionNodeName = (index: number, defaultFlow: boolean | undefined): string => {
|
export const getDefaultConditionNodeName = (
|
||||||
|
index: number,
|
||||||
|
defaultFlow: boolean | undefined
|
||||||
|
): string => {
|
||||||
if (defaultFlow) {
|
if (defaultFlow) {
|
||||||
return '其它情况'
|
return '其它情况'
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +12,10 @@ export const getDefaultConditionNodeName = (index: number, defaultFlow: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取包容分支条件节点默认的名称
|
// 获取包容分支条件节点默认的名称
|
||||||
export const getDefaultInclusiveConditionNodeName = (index: number, defaultFlow: boolean | undefined): string => {
|
export const getDefaultInclusiveConditionNodeName = (
|
||||||
|
index: number,
|
||||||
|
defaultFlow: boolean | undefined
|
||||||
|
): string => {
|
||||||
if (defaultFlow) {
|
if (defaultFlow) {
|
||||||
return '其它情况'
|
return '其它情况'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.router {
|
.router {
|
||||||
color: #ca3a31
|
color: #ca3a31;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transactor {
|
.transactor {
|
||||||
|
|
@ -303,7 +303,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.router-node {
|
&.router-node {
|
||||||
color: #ca3a31
|
color: #ca3a31;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.transactor-task {
|
&.transactor-task {
|
||||||
|
|
@ -762,14 +762,15 @@
|
||||||
|
|
||||||
// iconfont 样式
|
// iconfont 样式
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 4495938 */
|
font-family: 'iconfont'; /* Project id 4495938 */
|
||||||
src: url('iconfont.woff2?t=1737639517142') format('woff2'),
|
src:
|
||||||
url('iconfont.woff?t=1737639517142') format('woff'),
|
url('iconfont.woff2?t=1737639517142') format('woff2'),
|
||||||
url('iconfont.ttf?t=1737639517142') format('truetype');
|
url('iconfont.woff?t=1737639517142') format('woff'),
|
||||||
|
url('iconfont.ttf?t=1737639517142') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
font-family: "iconfont" !important;
|
font-family: 'iconfont' !important;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
|
|
@ -777,49 +778,49 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-trigger:before {
|
.icon-trigger:before {
|
||||||
content: "\e6d3";
|
content: '\e6d3';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-router:before {
|
.icon-router:before {
|
||||||
content: "\e6b2";
|
content: '\e6b2';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-delay:before {
|
.icon-delay:before {
|
||||||
content: "\e600";
|
content: '\e600';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-start-user:before {
|
.icon-start-user:before {
|
||||||
content: "\e679";
|
content: '\e679';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-inclusive:before {
|
.icon-inclusive:before {
|
||||||
content: "\e602";
|
content: '\e602';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-copy:before {
|
.icon-copy:before {
|
||||||
content: "\e7eb";
|
content: '\e7eb';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-transactor:before {
|
.icon-transactor:before {
|
||||||
content: "\e61c";
|
content: '\e61c';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-exclusive:before {
|
.icon-exclusive:before {
|
||||||
content: "\e717";
|
content: '\e717';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-approve:before {
|
.icon-approve:before {
|
||||||
content: "\e715";
|
content: '\e715';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-parallel:before {
|
.icon-parallel:before {
|
||||||
content: "\e688";
|
content: '\e688';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-async-child-process:before {
|
.icon-async-child-process:before {
|
||||||
content: "\e6f2";
|
content: '\e6f2';
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-child-process:before {
|
.icon-child-process:before {
|
||||||
content: "\e6c1";
|
content: '\e6c1';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
* Verify 验证码组件
|
* Verify 验证码组件
|
||||||
* @description 分发验证码使用
|
* @description 分发验证码使用
|
||||||
* */
|
* */
|
||||||
import {VerifyPictureWord, VerifyPoints, VerifySlide} from './Verify'
|
import { VerifyPictureWord, VerifyPoints, VerifySlide } from './Verify'
|
||||||
import { computed, ref, toRefs, watchEffect } from 'vue'
|
import { computed, ref, toRefs, watchEffect } from 'vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -443,4 +443,4 @@ export default {
|
||||||
content: ' ';
|
content: ' ';
|
||||||
inset: 0;
|
inset: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,4 @@ import VerifySlide from './VerifySlide.vue'
|
||||||
import VerifyPoints from './VerifyPoints.vue'
|
import VerifyPoints from './VerifyPoints.vue'
|
||||||
import VerifyPictureWord from './VerifyPictureWord.vue'
|
import VerifyPictureWord from './VerifyPictureWord.vue'
|
||||||
|
|
||||||
export { VerifySlide, VerifyPoints, VerifyPictureWord }
|
export { VerifySlide, VerifyPoints, VerifyPictureWord }
|
||||||
|
|
|
||||||
|
|
@ -1254,11 +1254,11 @@
|
||||||
"allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"]
|
"allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"]
|
||||||
},
|
},
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
"name": "value",
|
"name": "value",
|
||||||
"type": "Integer",
|
"type": "Integer",
|
||||||
"isBody": true
|
"isBody": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,20 +23,20 @@
|
||||||
|
|
||||||
export default function customTranslate(translations) {
|
export default function customTranslate(translations) {
|
||||||
return function (template, replacements) {
|
return function (template, replacements) {
|
||||||
replacements = replacements || {};
|
replacements = replacements || {}
|
||||||
// 将模板和翻译字典的键统一转换为小写进行匹配
|
// 将模板和翻译字典的键统一转换为小写进行匹配
|
||||||
const lowerTemplate = template.toLowerCase();
|
const lowerTemplate = template.toLowerCase()
|
||||||
const translation = Object.keys(translations).find(key => key.toLowerCase() === lowerTemplate);
|
const translation = Object.keys(translations).find((key) => key.toLowerCase() === lowerTemplate)
|
||||||
|
|
||||||
// 如果找到匹配的翻译,使用翻译后的模板
|
// 如果找到匹配的翻译,使用翻译后的模板
|
||||||
if (translation) {
|
if (translation) {
|
||||||
template = translations[translation];
|
template = translations[translation]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 替换模板中的占位符
|
// 替换模板中的占位符
|
||||||
return template.replace(/{([^}]+)}/g, function (_, key) {
|
return template.replace(/{([^}]+)}/g, function (_, key) {
|
||||||
// 如果替换值存在,返回替换值;否则返回原始占位符
|
// 如果替换值存在,返回替换值;否则返回原始占位符
|
||||||
return replacements[key] !== undefined ? replacements[key] : `{${key}}`;
|
return replacements[key] !== undefined ? replacements[key] : `{${key}}`
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,15 @@
|
||||||
@use './process-panel.scss';
|
@use './process-panel.scss';
|
||||||
|
|
||||||
$success-color: #4eb819;
|
$success-color: #4eb819;
|
||||||
$primary-color: #409EFF;
|
$primary-color: #409eff;
|
||||||
$danger-color: #F56C6C;
|
$danger-color: #f56c6c;
|
||||||
$cancel-color: #909399;
|
$cancel-color: #909399;
|
||||||
|
|
||||||
.process-viewer {
|
.process-viewer {
|
||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid #EFEFEF;
|
border: 1px solid #efefef;
|
||||||
background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat!important;
|
background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+')
|
||||||
|
repeat !important;
|
||||||
|
|
||||||
.success-arrow {
|
.success-arrow {
|
||||||
fill: $success-color;
|
fill: $success-color;
|
||||||
|
|
@ -23,7 +24,7 @@ $cancel-color: #909399;
|
||||||
|
|
||||||
.success.djs-connection {
|
.success.djs-connection {
|
||||||
.djs-visual path {
|
.djs-visual path {
|
||||||
stroke: $success-color!important;
|
stroke: $success-color !important;
|
||||||
//marker-end: url(#sequenceflow-end-white-success)!important;
|
//marker-end: url(#sequenceflow-end-white-success)!important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,82 +37,84 @@ $cancel-color: #909399;
|
||||||
|
|
||||||
.success.djs-shape {
|
.success.djs-shape {
|
||||||
.djs-visual rect {
|
.djs-visual rect {
|
||||||
stroke: $success-color!important;
|
stroke: $success-color !important;
|
||||||
fill: $success-color!important;
|
fill: $success-color !important;
|
||||||
fill-opacity: 0.15!important;
|
fill-opacity: 0.15 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.djs-visual polygon {
|
.djs-visual polygon {
|
||||||
stroke: $success-color!important;
|
stroke: $success-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.djs-visual path:nth-child(2) {
|
.djs-visual path:nth-child(2) {
|
||||||
stroke: $success-color!important;
|
stroke: $success-color !important;
|
||||||
fill: $success-color!important;
|
fill: $success-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.djs-visual circle {
|
.djs-visual circle {
|
||||||
stroke: $success-color!important;
|
stroke: $success-color !important;
|
||||||
fill: $success-color!important;
|
fill: $success-color !important;
|
||||||
fill-opacity: 0.15!important;
|
fill-opacity: 0.15 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary.djs-shape {
|
.primary.djs-shape {
|
||||||
.djs-visual rect {
|
.djs-visual rect {
|
||||||
stroke: $primary-color!important;
|
stroke: $primary-color !important;
|
||||||
fill: $primary-color!important;
|
fill: $primary-color !important;
|
||||||
fill-opacity: 0.15!important;
|
fill-opacity: 0.15 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.djs-visual polygon {
|
.djs-visual polygon {
|
||||||
stroke: $primary-color!important;
|
stroke: $primary-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.djs-visual circle {
|
.djs-visual circle {
|
||||||
stroke: $primary-color!important;
|
stroke: $primary-color !important;
|
||||||
fill: $primary-color!important;
|
fill: $primary-color !important;
|
||||||
fill-opacity: 0.15!important;
|
fill-opacity: 0.15 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.danger.djs-shape {
|
.danger.djs-shape {
|
||||||
.djs-visual rect {
|
.djs-visual rect {
|
||||||
stroke: $danger-color!important;
|
stroke: $danger-color !important;
|
||||||
fill: $danger-color!important;
|
fill: $danger-color !important;
|
||||||
fill-opacity: 0.15!important;
|
fill-opacity: 0.15 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.djs-visual polygon {
|
.djs-visual polygon {
|
||||||
stroke: $danger-color!important;
|
stroke: $danger-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.djs-visual circle {
|
.djs-visual circle {
|
||||||
stroke: $danger-color!important;
|
stroke: $danger-color !important;
|
||||||
fill: $danger-color!important;
|
fill: $danger-color !important;
|
||||||
fill-opacity: 0.15!important;
|
fill-opacity: 0.15 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cancel.djs-shape {
|
.cancel.djs-shape {
|
||||||
.djs-visual rect {
|
.djs-visual rect {
|
||||||
stroke: $cancel-color!important;
|
stroke: $cancel-color !important;
|
||||||
fill: $cancel-color!important;
|
fill: $cancel-color !important;
|
||||||
fill-opacity: 0.15!important;
|
fill-opacity: 0.15 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.djs-visual polygon {
|
.djs-visual polygon {
|
||||||
stroke: $cancel-color!important;
|
stroke: $cancel-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.djs-visual circle {
|
.djs-visual circle {
|
||||||
stroke: $cancel-color!important;
|
stroke: $cancel-color !important;
|
||||||
fill: $cancel-color!important;
|
fill: $cancel-color !important;
|
||||||
fill-opacity: 0.15!important;
|
fill-opacity: 0.15 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.process-viewer .djs-tooltip-container, .process-viewer .djs-overlay-container, .process-viewer .djs-palette {
|
.process-viewer .djs-tooltip-container,
|
||||||
|
.process-viewer .djs-overlay-container,
|
||||||
|
.process-viewer .djs-palette {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,8 +167,8 @@ service.interceptors.response.use(
|
||||||
cb()
|
cb()
|
||||||
})
|
})
|
||||||
requestList = []
|
requestList = []
|
||||||
if ((config!.headers || {}).isEncrypt){
|
if ((config!.headers || {}).isEncrypt) {
|
||||||
(config!.headers || {}).isEncrypted = true
|
;(config!.headers || {}).isEncrypted = true
|
||||||
}
|
}
|
||||||
return service(config)
|
return service(config)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export function useWatermark(appendEl: HTMLElement | null = document.body) {
|
||||||
const id = domSymbol.toString()
|
const id = domSymbol.toString()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
let watermarkStr = ''
|
let watermarkStr = ''
|
||||||
|
|
||||||
const clear = () => {
|
const clear = () => {
|
||||||
const domId = document.getElementById(id)
|
const domId = document.getElementById(id)
|
||||||
if (domId) {
|
if (domId) {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,10 @@ onMounted(() => {
|
||||||
watch(
|
watch(
|
||||||
() => collapse.value,
|
() => collapse.value,
|
||||||
(collapse: boolean) => {
|
(collapse: boolean) => {
|
||||||
if (getLayoutRenderMode(unref(layout)) === 'topLeft' || getLayoutRenderMode(unref(layout)) === 'cutMenu') {
|
if (
|
||||||
|
getLayoutRenderMode(unref(layout)) === 'topLeft' ||
|
||||||
|
getLayoutRenderMode(unref(layout)) === 'cutMenu'
|
||||||
|
) {
|
||||||
show.value = true
|
show.value = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,7 @@ import { hasOneShowingChild } from './helper'
|
||||||
import { isUrl } from '@/utils/is'
|
import { isUrl } from '@/utils/is'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { createRouteLocation, resolveDynamicPath } from '@/utils/routeParams'
|
import { createRouteLocation, resolveDynamicPath } from '@/utils/routeParams'
|
||||||
import {
|
import { isHeaderNavLayout, isHorizontalMenuLayout, isTwoColumnLayout } from '@/utils/layout'
|
||||||
isHeaderNavLayout,
|
|
||||||
isHorizontalMenuLayout,
|
|
||||||
isTwoColumnLayout
|
|
||||||
} from '@/utils/layout'
|
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
import { pathResolve } from '@/utils/routerHelper'
|
import { pathResolve } from '@/utils/routerHelper'
|
||||||
import {
|
import {
|
||||||
|
|
@ -69,8 +65,9 @@ export default defineComponent({
|
||||||
|
|
||||||
const menuRef = ref<InstanceType<typeof ElMenu>>()
|
const menuRef = ref<InstanceType<typeof ElMenu>>()
|
||||||
|
|
||||||
const menuMode = computed((): 'vertical' | 'horizontal' =>
|
const menuMode = computed(
|
||||||
props.mode || (isHorizontalMenuLayout(unref(layout)) ? 'horizontal' : 'vertical')
|
(): 'vertical' | 'horizontal' =>
|
||||||
|
props.mode || (isHorizontalMenuLayout(unref(layout)) ? 'horizontal' : 'vertical')
|
||||||
)
|
)
|
||||||
|
|
||||||
const collapse = computed(() => appStore.getCollapse)
|
const collapse = computed(() => appStore.getCollapse)
|
||||||
|
|
@ -102,7 +99,8 @@ export default defineComponent({
|
||||||
|
|
||||||
const routers = computed(() => {
|
const routers = computed(() => {
|
||||||
const sourceRouters =
|
const sourceRouters =
|
||||||
props.menus || (props.split ? permissionStore.getMenuTabRouters : permissionStore.getRouters)
|
props.menus ||
|
||||||
|
(props.split ? permissionStore.getMenuTabRouters : permissionStore.getRouters)
|
||||||
if (!props.rootOnly) {
|
if (!props.rootOnly) {
|
||||||
return sourceRouters
|
return sourceRouters
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +111,10 @@ export default defineComponent({
|
||||||
const { meta, path } = unref(currentRoute)
|
const { meta, path } = unref(currentRoute)
|
||||||
const currentPath = (meta.activeMenu as string) || path
|
const currentPath = (meta.activeMenu as string) || path
|
||||||
if (props.rootOnly) {
|
if (props.rootOnly) {
|
||||||
return permissionStore.getMenuRootPath || getRootMenuActivePath(permissionStore.getRouters, currentPath)
|
return (
|
||||||
|
permissionStore.getMenuRootPath ||
|
||||||
|
getRootMenuActivePath(permissionStore.getRouters, currentPath)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// if set path, the sidebar will highlight the path you set
|
// if set path, the sidebar will highlight the path you set
|
||||||
if (meta.activeMenu) {
|
if (meta.activeMenu) {
|
||||||
|
|
@ -134,7 +135,10 @@ export default defineComponent({
|
||||||
props.theme === 'header' ? 'var(--el-color-primary)' : 'var(--left-menu-text-active-color)'
|
props.theme === 'header' ? 'var(--el-color-primary)' : 'var(--left-menu-text-active-color)'
|
||||||
)
|
)
|
||||||
|
|
||||||
const getFirstChildPath = (route: AppRouteRecordRaw, parentPath: string): string | undefined => {
|
const getFirstChildPath = (
|
||||||
|
route: AppRouteRecordRaw,
|
||||||
|
parentPath: string
|
||||||
|
): string | undefined => {
|
||||||
const firstChild = route.children?.find((child) => !child.meta?.hidden)
|
const firstChild = route.children?.find((child) => !child.meta?.hidden)
|
||||||
if (!firstChild) {
|
if (!firstChild) {
|
||||||
return undefined
|
return undefined
|
||||||
|
|
@ -218,7 +222,8 @@ export default defineComponent({
|
||||||
|
|
||||||
const setSplitMenus = (targetPath: string) => {
|
const setSplitMenus = (targetPath: string) => {
|
||||||
const rootInfo = getRootMenuRoute(permissionStore.getRouters, targetPath)
|
const rootInfo = getRootMenuRoute(permissionStore.getRouters, targetPath)
|
||||||
const rootPath = rootInfo?.fullPath || getRootMenuActivePath(permissionStore.getRouters, targetPath)
|
const rootPath =
|
||||||
|
rootInfo?.fullPath || getRootMenuActivePath(permissionStore.getRouters, targetPath)
|
||||||
const rootRoute = rootInfo?.route
|
const rootRoute = rootInfo?.route
|
||||||
const children = rootRoute?.children?.length
|
const children = rootRoute?.children?.length
|
||||||
? cloneDeep(rootRoute.children).map((child) => {
|
? cloneDeep(rootRoute.children).map((child) => {
|
||||||
|
|
@ -386,7 +391,8 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
const targetPath =
|
const targetPath =
|
||||||
props.rootOnly && routeInfo?.route.children?.length
|
props.rootOnly && routeInfo?.route.children?.length
|
||||||
? ((routeInfo.route.redirect as string) || getFirstChildPath(routeInfo.route, routeInfo.fullPath))
|
? (routeInfo.route.redirect as string) ||
|
||||||
|
getFirstChildPath(routeInfo.route, routeInfo.fullPath)
|
||||||
: index
|
: index
|
||||||
|
|
||||||
if (targetPath) {
|
if (targetPath) {
|
||||||
|
|
@ -541,9 +547,13 @@ export default defineComponent({
|
||||||
'h-[100%] overflow-hidden flex-col bg-[var(--left-menu-bg-color)]',
|
'h-[100%] overflow-hidden flex-col bg-[var(--left-menu-bg-color)]',
|
||||||
{
|
{
|
||||||
'w-[var(--left-menu-min-width)]':
|
'w-[var(--left-menu-min-width)]':
|
||||||
unref(collapse) && !isTwoColumnLayout(unref(layout)) && unref(menuMode) !== 'horizontal',
|
unref(collapse) &&
|
||||||
|
!isTwoColumnLayout(unref(layout)) &&
|
||||||
|
unref(menuMode) !== 'horizontal',
|
||||||
'w-[var(--left-menu-max-width)]':
|
'w-[var(--left-menu-max-width)]':
|
||||||
!unref(collapse) && !isTwoColumnLayout(unref(layout)) && unref(menuMode) !== 'horizontal'
|
!unref(collapse) &&
|
||||||
|
!isTwoColumnLayout(unref(layout)) &&
|
||||||
|
unref(menuMode) !== 'horizontal'
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -642,13 +652,12 @@ $prefix-cls: #{$namespace}-menu;
|
||||||
height: calc(var(--top-tool-height)) !important;
|
height: calc(var(--top-tool-height)) !important;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
overflow-x: hidden;
|
overflow: hidden;
|
||||||
overflow-y: hidden;
|
|
||||||
|
|
||||||
:deep(.#{$elNamespace}-menu--horizontal) {
|
:deep(.#{$elNamespace}-menu--horizontal) {
|
||||||
height: calc(var(--top-tool-height));
|
height: calc(var(--top-tool-height));
|
||||||
border-bottom: none;
|
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
|
border-bottom: none;
|
||||||
// 重新设置底部高亮颜色
|
// 重新设置底部高亮颜色
|
||||||
& > .#{$elNamespace}-sub-menu.is-active {
|
& > .#{$elNamespace}-sub-menu.is-active {
|
||||||
.#{$elNamespace}-sub-menu__title {
|
.#{$elNamespace}-sub-menu__title {
|
||||||
|
|
|
||||||
|
|
@ -33,14 +33,12 @@ export const useRenderMenuItem = () =>
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!children.length ||
|
!children.length ||
|
||||||
oneShowingChild &&
|
(oneShowingChild &&
|
||||||
(!onlyOneChild?.children || onlyOneChild?.noShowingChildren) &&
|
(!onlyOneChild?.children || onlyOneChild?.noShowingChildren) &&
|
||||||
!meta?.alwaysShow
|
!meta?.alwaysShow)
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<ElMenuItem
|
<ElMenuItem index={resolvedOnlyOneChildPath}>
|
||||||
index={resolvedOnlyOneChildPath}
|
|
||||||
>
|
|
||||||
{{
|
{{
|
||||||
default: () => renderMenuTitle(onlyOneChild ? onlyOneChild?.meta : meta)
|
default: () => renderMenuTitle(onlyOneChild ? onlyOneChild?.meta : meta)
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,9 @@ export const getRootMenuRoute = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getRootMenuActivePath = (
|
export const getRootMenuActivePath = (routes: AppRouteRecordRaw[], targetPath: string): string => {
|
||||||
routes: AppRouteRecordRaw[],
|
return (
|
||||||
targetPath: string
|
getRootMenuRoute(routes, targetPath)?.fullPath ||
|
||||||
): string => {
|
getRootPath(normalizeMenuTargetPath(targetPath))
|
||||||
return getRootMenuRoute(routes, targetPath)?.fullPath || getRootPath(normalizeMenuTargetPath(targetPath))
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,7 @@ import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { isUrl } from '@/utils/is'
|
import { isUrl } from '@/utils/is'
|
||||||
import { createRouteLocation } from '@/utils/routeParams'
|
import { createRouteLocation } from '@/utils/routeParams'
|
||||||
import { getRootPath, isHeaderMixedNavLayout, isTwoColumnLayout } from '@/utils/layout'
|
import { getRootPath, isHeaderMixedNavLayout, isTwoColumnLayout } from '@/utils/layout'
|
||||||
import {
|
import { getRootMenuRoute, normalizeMenuTargetPath } from '@/layout/components/Menu/src/menuRoute'
|
||||||
getRootMenuRoute,
|
|
||||||
normalizeMenuTargetPath
|
|
||||||
} from '@/layout/components/Menu/src/menuRoute'
|
|
||||||
|
|
||||||
const { getPrefixCls, variables } = useDesign()
|
const { getPrefixCls, variables } = useDesign()
|
||||||
|
|
||||||
|
|
@ -58,7 +55,9 @@ export default defineComponent({
|
||||||
return getRootMenuRoute(unref(routers), targetPath)
|
return getRootMenuRoute(unref(routers), targetPath)
|
||||||
})
|
})
|
||||||
|
|
||||||
const rootPath = computed(() => unref(activeRootInfo)?.fullPath || getRootPath(unref(currentMenuPath)))
|
const rootPath = computed(
|
||||||
|
() => unref(activeRootInfo)?.fullPath || getRootPath(unref(currentMenuPath))
|
||||||
|
)
|
||||||
|
|
||||||
const activeRootRoute = computed(() => unref(activeRootInfo)?.route)
|
const activeRootRoute = computed(() => unref(activeRootInfo)?.route)
|
||||||
|
|
||||||
|
|
@ -125,7 +124,9 @@ export default defineComponent({
|
||||||
const isSameMenuRouters = (left: AppRouteRecordRaw[], right: AppRouteRecordRaw[]): boolean => {
|
const isSameMenuRouters = (left: AppRouteRecordRaw[], right: AppRouteRecordRaw[]): boolean => {
|
||||||
return (
|
return (
|
||||||
left.length === right.length &&
|
left.length === right.length &&
|
||||||
left.every((route, index) => route.path === right[index]?.path && route.name === right[index]?.name)
|
left.every(
|
||||||
|
(route, index) => route.path === right[index]?.path && route.name === right[index]?.name
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +220,8 @@ export default defineComponent({
|
||||||
const children = getVisibleChildren(item)
|
const children = getVisibleChildren(item)
|
||||||
if (children.length) {
|
if (children.length) {
|
||||||
if (newPath === oldPath || !unref(showMenu)) {
|
if (newPath === oldPath || !unref(showMenu)) {
|
||||||
showMenu.value = unref(fixedMenu) || unref(headerMixed) || unref(twoColumn) ? true : !unref(showMenu)
|
showMenu.value =
|
||||||
|
unref(fixedMenu) || unref(headerMixed) || unref(twoColumn) ? true : !unref(showMenu)
|
||||||
}
|
}
|
||||||
if (unref(showMenu)) {
|
if (unref(showMenu)) {
|
||||||
setExtraMenuRouters(buildExtraMenuRouters(children, unref(tabActive)))
|
setExtraMenuRouters(buildExtraMenuRouters(children, unref(tabActive)))
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,8 @@ const moveToTarget = (currentTag: RouteLocationNormalizedLoaded) => {
|
||||||
} else {
|
} else {
|
||||||
// find preTag and nextTag
|
// find preTag and nextTag
|
||||||
const currentIndex: number = tagList.findIndex(
|
const currentIndex: number = tagList.findIndex(
|
||||||
(item) => (item?.to as RouteLocationNormalizedLoaded | undefined)?.fullPath === currentTag.fullPath
|
(item) =>
|
||||||
|
(item?.to as RouteLocationNormalizedLoaded | undefined)?.fullPath === currentTag.fullPath
|
||||||
)
|
)
|
||||||
if (currentIndex < 0) return
|
if (currentIndex < 0) return
|
||||||
const tgsRefs = document.getElementsByClassName(`${prefixCls}__item`)
|
const tgsRefs = document.getElementsByClassName(`${prefixCls}__item`)
|
||||||
|
|
@ -376,10 +377,10 @@ watch(
|
||||||
<Icon
|
<Icon
|
||||||
v-if="
|
v-if="
|
||||||
tagsViewIcon &&
|
tagsViewIcon &&
|
||||||
(item?.meta?.icon ||
|
(item?.meta?.icon ||
|
||||||
(item?.matched &&
|
(item?.matched &&
|
||||||
item.matched[0] &&
|
item.matched[0] &&
|
||||||
item.matched[item.matched.length - 1].meta?.icon))
|
item.matched[item.matched.length - 1].meta?.icon))
|
||||||
"
|
"
|
||||||
:icon="item?.meta?.icon || item.matched[item.matched.length - 1].meta.icon"
|
:icon="item?.meta?.icon || item.matched[item.matched.length - 1].meta.icon"
|
||||||
:size="12"
|
:size="12"
|
||||||
|
|
@ -387,7 +388,7 @@ watch(
|
||||||
/>
|
/>
|
||||||
{{
|
{{
|
||||||
t(item?.meta?.title as string) +
|
t(item?.meta?.title as string) +
|
||||||
(item?.meta?.titleSuffix ? ` (${item?.meta?.titleSuffix})` : '')
|
(item?.meta?.titleSuffix ? ` (${item?.meta?.titleSuffix})` : '')
|
||||||
}}
|
}}
|
||||||
<Icon
|
<Icon
|
||||||
:class="`${prefixCls}__item--close`"
|
:class="`${prefixCls}__item--close`"
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,11 @@ export default defineComponent({
|
||||||
) : undefined}
|
) : undefined}
|
||||||
<div class="h-full flex items-center">
|
<div class="h-full flex items-center">
|
||||||
{hasTenantVisitPermission.value ? <TenantVisit /> : undefined}
|
{hasTenantVisitPermission.value ? <TenantVisit /> : undefined}
|
||||||
<div class="v-setting custom-hover" title={t('setting.projectSetting')} onClick={openSetting}>
|
<div
|
||||||
|
class="v-setting custom-hover"
|
||||||
|
title={t('setting.projectSetting')}
|
||||||
|
onClick={openSetting}
|
||||||
|
>
|
||||||
<Icon color="var(--top-header-text-color)" size={18} icon="ep:setting" />
|
<Icon color="var(--top-header-text-color)" size={18} icon="ep:setting" />
|
||||||
</div>
|
</div>
|
||||||
{screenfull.value ? (
|
{screenfull.value ? (
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ const handleLock = async () => {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
:global(.v-lock-dialog) {
|
:global(.v-lock-dialog) {
|
||||||
@media (max-width: 767px) {
|
@media (width <= 767px) {
|
||||||
max-width: calc(100vw - 16px);
|
max-width: calc(100vw - 16px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,7 @@ $error-color: #ed6f6f;
|
||||||
font-size: 90px;
|
font-size: 90px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $screen-lg) {
|
@media screen and (min-width: $screen-lg) {
|
||||||
span:not(.meridiem) {
|
span:not(.meridiem) {
|
||||||
font-size: 220px;
|
font-size: 220px;
|
||||||
|
|
@ -216,6 +217,7 @@ $error-color: #ed6f6f;
|
||||||
font-size: 260px;
|
font-size: 260px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: $screen-2xl) {
|
@media screen and (min-width: $screen-2xl) {
|
||||||
span:not(.meridiem) {
|
span:not(.meridiem) {
|
||||||
font-size: 320px;
|
font-size: 320px;
|
||||||
|
|
@ -230,7 +232,7 @@ $error-color: #ed6f6f;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgb(0 0 0 / 50%);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,7 @@ import AppView from './AppView.vue'
|
||||||
import ToolHeader from './ToolHeader.vue'
|
import ToolHeader from './ToolHeader.vue'
|
||||||
import { ElScrollbar } from 'element-plus'
|
import { ElScrollbar } from 'element-plus'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import {
|
import { isHeaderMixedNavLayout, isMixedNavLayout, isTwoColumnLayout } from '@/utils/layout'
|
||||||
isHeaderMixedNavLayout,
|
|
||||||
isMixedNavLayout,
|
|
||||||
isTwoColumnLayout
|
|
||||||
} from '@/utils/layout'
|
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
|
|
@ -251,7 +247,8 @@ export const useRenderLayout = () => {
|
||||||
const renderCutMenu = () => {
|
const renderCutMenu = () => {
|
||||||
const showHeaderMenu = isHeaderMixedNavLayout(layout.value)
|
const showHeaderMenu = isHeaderMixedNavLayout(layout.value)
|
||||||
const fixedTwoColumnMenu = fixedMenu.value || isTwoColumnLayout(layout.value)
|
const fixedTwoColumnMenu = fixedMenu.value || isTwoColumnLayout(layout.value)
|
||||||
const showTwoColumnExtraMenu = fixedTwoColumnMenu && permissionStore.getMenuTabRouters.length > 0
|
const showTwoColumnExtraMenu =
|
||||||
|
fixedTwoColumnMenu && permissionStore.getMenuTabRouters.length > 0
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div class="relative flex items-center bg-[var(--top-header-bg-color)] layout-border__bottom">
|
<div class="relative flex items-center bg-[var(--top-header-bg-color)] layout-border__bottom">
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,6 @@ export const PREDEFINE_COLORS = [
|
||||||
'#711f57'
|
'#711f57'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mixes two colors.
|
* Mixes two colors.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -172,43 +172,43 @@ export const once = function (el: HTMLElement, event: string, fn: EventListener)
|
||||||
export const getStyle =
|
export const getStyle =
|
||||||
ieVersion < 9
|
ieVersion < 9
|
||||||
? function (element: Element | any, styleName: string) {
|
? function (element: Element | any, styleName: string) {
|
||||||
if (isServer) return
|
if (isServer) return
|
||||||
if (!element || !styleName) return null
|
if (!element || !styleName) return null
|
||||||
styleName = camelCase(styleName)
|
styleName = camelCase(styleName)
|
||||||
if (styleName === 'float') {
|
if (styleName === 'float') {
|
||||||
styleName = 'styleFloat'
|
styleName = 'styleFloat'
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
switch (styleName) {
|
switch (styleName) {
|
||||||
case 'opacity':
|
case 'opacity':
|
||||||
try {
|
try {
|
||||||
return element.filters.item('alpha').opacity / 100
|
return element.filters.item('alpha').opacity / 100
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return 1.0
|
return 1.0
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return element.style[styleName] || element.currentStyle
|
return element.style[styleName] || element.currentStyle
|
||||||
? element.currentStyle[styleName]
|
? element.currentStyle[styleName]
|
||||||
: null
|
: null
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return element.style[styleName]
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
return element.style[styleName]
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
: function (element: Element | any, styleName: string) {
|
: function (element: Element | any, styleName: string) {
|
||||||
if (isServer) return
|
if (isServer) return
|
||||||
if (!element || !styleName) return null
|
if (!element || !styleName) return null
|
||||||
styleName = camelCase(styleName)
|
styleName = camelCase(styleName)
|
||||||
if (styleName === 'float') {
|
if (styleName === 'float') {
|
||||||
styleName = 'cssFloat'
|
styleName = 'cssFloat'
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const computed = (document as any).defaultView.getComputedStyle(element, '')
|
||||||
|
return element.style[styleName] || computed ? computed[styleName] : null
|
||||||
|
} catch (e) {
|
||||||
|
return element.style[styleName]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
const computed = (document as any).defaultView.getComputedStyle(element, '')
|
|
||||||
return element.style[styleName] || computed ? computed[styleName] : null
|
|
||||||
} catch (e) {
|
|
||||||
return element.style[styleName]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
export function setStyle(element: Element | any, styleName: any, value: any) {
|
export function setStyle(element: Element | any, styleName: any, value: any) {
|
||||||
|
|
@ -287,4 +287,3 @@ export const isInContainer = (el: Element, container: any) => {
|
||||||
elRect.left < containerRect.right
|
elRect.left < containerRect.right
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,9 @@ export function formatPast2(ms: number): string {
|
||||||
*/
|
*/
|
||||||
export function formatSeconds(seconds: number): string {
|
export function formatSeconds(seconds: number): string {
|
||||||
const s = Math.max(0, Math.floor(seconds || 0))
|
const s = Math.max(0, Math.floor(seconds || 0))
|
||||||
const mm = Math.floor(s / 60).toString().padStart(2, '0')
|
const mm = Math.floor(s / 60)
|
||||||
|
.toString()
|
||||||
|
.padStart(2, '0')
|
||||||
const ss = (s % 60).toString().padStart(2, '0')
|
const ss = (s % 60).toString().padStart(2, '0')
|
||||||
return `${mm}:${ss}`
|
return `${mm}:${ss}`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -215,10 +215,10 @@ export const generateUUID = () => {
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||||
let random = Math.random() * 16
|
let random = Math.random() * 16
|
||||||
if (timestamp > 0) {
|
if (timestamp > 0) {
|
||||||
random = (timestamp + random) % 16 | 0
|
random = ((timestamp + random) % 16) | 0
|
||||||
timestamp = Math.floor(timestamp / 16)
|
timestamp = Math.floor(timestamp / 16)
|
||||||
} else {
|
} else {
|
||||||
random = (performanceNow + random) % 16 | 0
|
random = ((performanceNow + random) % 16) | 0
|
||||||
performanceNow = Math.floor(performanceNow / 16)
|
performanceNow = Math.floor(performanceNow / 16)
|
||||||
}
|
}
|
||||||
return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16)
|
return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16)
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ export const isClient = !isServer
|
||||||
export const isUrl = (path: string): boolean => {
|
export const isUrl = (path: string): boolean => {
|
||||||
// fix:修复hash路由无法跳转的问题
|
// fix:修复hash路由无法跳转的问题
|
||||||
const reg =
|
const reg =
|
||||||
/(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%#\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/
|
/(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%#\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/
|
||||||
return reg.test(path)
|
return reg.test(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,9 @@ export const isHeaderNavLayout = (layout?: LayoutType | string | null): boolean
|
||||||
|
|
||||||
export const isHorizontalMenuLayout = (layout?: LayoutType | string | null): boolean => {
|
export const isHorizontalMenuLayout = (layout?: LayoutType | string | null): boolean => {
|
||||||
const normalized = normalizeLayout(layout)
|
const normalized = normalizeLayout(layout)
|
||||||
return normalized === 'header-nav' || normalized === 'mixed-nav' || normalized === 'header-mixed-nav'
|
return (
|
||||||
|
normalized === 'header-nav' || normalized === 'mixed-nav' || normalized === 'header-mixed-nav'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isMixedNavLayout = (layout?: LayoutType | string | null): boolean => {
|
export const isMixedNavLayout = (layout?: LayoutType | string | null): boolean => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||||
import {hasPermission} from "@/directives/permission/hasPermi";
|
import { hasPermission } from '@/directives/permission/hasPermi'
|
||||||
|
|
||||||
|
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,7 @@ export const parseQueryString = (queryString = ''): Record<string, any> => {
|
||||||
return qs.parse(queryString.replace(/^\?/, '')) as Record<string, any>
|
return qs.parse(queryString.replace(/^\?/, '')) as Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const splitRoutePath = (
|
export const splitRoutePath = (rawPath: string | null | undefined): ParsedRouteLocation => {
|
||||||
rawPath: string | null | undefined
|
|
||||||
): ParsedRouteLocation => {
|
|
||||||
if (!rawPath) {
|
if (!rawPath) {
|
||||||
return { path: '' }
|
return { path: '' }
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +111,8 @@ export const splitDynamicRouteParams = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isRepeatableParam = (matched: string): boolean => matched.endsWith('*') || matched.endsWith('+')
|
const isRepeatableParam = (matched: string): boolean =>
|
||||||
|
matched.endsWith('*') || matched.endsWith('+')
|
||||||
|
|
||||||
const encodeRouteParam = (matched: string, value: any): string => {
|
const encodeRouteParam = (matched: string, value: any): string => {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ const iconHouse = useIcon({ icon: 'ep:house' })
|
||||||
const iconAvatar = useIcon({ icon: 'ep:avatar' })
|
const iconAvatar = useIcon({ icon: 'ep:avatar' })
|
||||||
const iconLock = useIcon({ icon: 'ep:lock' })
|
const iconLock = useIcon({ icon: 'ep:lock' })
|
||||||
const formLogin = ref()
|
const formLogin = ref()
|
||||||
const {validForm} = useFormValid(formLogin)
|
const { validForm } = useFormValid(formLogin)
|
||||||
const { handleBackLogin, getLoginState } = useLoginState()
|
const { handleBackLogin, getLoginState } = useLoginState()
|
||||||
const { currentRoute, push } = useRouter()
|
const { currentRoute, push } = useRouter()
|
||||||
const permissionStore = usePermissionStore()
|
const permissionStore = usePermissionStore()
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,12 @@ import QrCodeForm from './QrCodeForm.vue'
|
||||||
import SSOLoginVue from './SSOLogin.vue'
|
import SSOLoginVue from './SSOLogin.vue'
|
||||||
import ForgetPasswordForm from './ForgetPasswordForm.vue'
|
import ForgetPasswordForm from './ForgetPasswordForm.vue'
|
||||||
|
|
||||||
export { LoginForm, MobileForm, LoginFormTitle, RegisterForm, QrCodeForm, SSOLoginVue, ForgetPasswordForm }
|
export {
|
||||||
|
LoginForm,
|
||||||
|
MobileForm,
|
||||||
|
LoginFormTitle,
|
||||||
|
RegisterForm,
|
||||||
|
QrCodeForm,
|
||||||
|
SSOLoginVue,
|
||||||
|
ForgetPasswordForm
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<el-card
|
<el-card body-class="" class="!w-80 !h-auto !rounded-10px !relative !flex !flex-col">
|
||||||
body-class=""
|
|
||||||
class="!w-80 !h-auto !rounded-10px !relative !flex !flex-col"
|
|
||||||
>
|
|
||||||
<div class="!flex !flex-row !justify-between">
|
<div class="!flex !flex-row !justify-between">
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" text bg v-if="detail?.status === AiImageStatusEnum.IN_PROGRESS">
|
<el-button type="primary" text bg v-if="detail?.status === AiImageStatusEnum.IN_PROGRESS">
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,11 @@
|
||||||
</div>
|
</div>
|
||||||
<el-space wrap class="mt-15px">
|
<el-space wrap class="mt-15px">
|
||||||
<div
|
<div
|
||||||
:class="selectModel === model.key ? 'w-110px overflow-hidden flex flex-col items-center border-3 border-solid border-#1293ff rounded-5px cursor-pointer' : 'w-110px overflow-hidden flex flex-col items-center border-3 border-solid border-transparent cursor-pointer'"
|
:class="
|
||||||
|
selectModel === model.key
|
||||||
|
? 'w-110px overflow-hidden flex flex-col items-center border-3 border-solid border-#1293ff rounded-5px cursor-pointer'
|
||||||
|
: 'w-110px overflow-hidden flex flex-col items-center border-3 border-solid border-transparent cursor-pointer'
|
||||||
|
"
|
||||||
v-for="model in Dall3Models"
|
v-for="model in Dall3Models"
|
||||||
:key="model.key"
|
:key="model.key"
|
||||||
>
|
>
|
||||||
|
|
@ -52,7 +56,11 @@
|
||||||
</div>
|
</div>
|
||||||
<el-space wrap class="mt-15px">
|
<el-space wrap class="mt-15px">
|
||||||
<div
|
<div
|
||||||
:class="style === imageStyle.key ? 'w-110px overflow-hidden flex flex-col items-center border-3 border-solid border-#1293ff rounded-5px cursor-pointer' : 'w-110px overflow-hidden flex flex-col items-center border-3 border-solid border-transparent cursor-pointer'"
|
:class="
|
||||||
|
style === imageStyle.key
|
||||||
|
? 'w-110px overflow-hidden flex flex-col items-center border-3 border-solid border-#1293ff rounded-5px cursor-pointer'
|
||||||
|
: 'w-110px overflow-hidden flex flex-col items-center border-3 border-solid border-transparent cursor-pointer'
|
||||||
|
"
|
||||||
v-for="imageStyle in Dall3StyleList"
|
v-for="imageStyle in Dall3StyleList"
|
||||||
:key="imageStyle.key"
|
:key="imageStyle.key"
|
||||||
>
|
>
|
||||||
|
|
@ -73,7 +81,11 @@
|
||||||
@click="handleSizeClick(imageSize)"
|
@click="handleSizeClick(imageSize)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:class="selectSize === imageSize.key ? 'flex flex-col items-center justify-center rounded-7px p-4px w-50px h-50px bg-white border-1 border-solid border-#1293ff' : 'flex flex-col items-center justify-center rounded-7px p-4px w-50px h-50px bg-white border-1 border-solid border-white'"
|
:class="
|
||||||
|
selectSize === imageSize.key
|
||||||
|
? 'flex flex-col items-center justify-center rounded-7px p-4px w-50px h-50px bg-white border-1 border-solid border-#1293ff'
|
||||||
|
: 'flex flex-col items-center justify-center rounded-7px p-4px w-50px h-50px bg-white border-1 border-solid border-white'
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<div :style="imageSize.style"></div>
|
<div :style="imageSize.style"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -229,4 +241,3 @@ const settingValues = async (detail: ImageVO) => {
|
||||||
/** 暴露组件方法 */
|
/** 暴露组件方法 */
|
||||||
defineExpose({ settingValues })
|
defineExpose({ settingValues })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,11 @@
|
||||||
@click="handleSizeClick(imageSize)"
|
@click="handleSizeClick(imageSize)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:class="selectSize === imageSize.key ? 'flex flex-col items-center justify-center rounded-7px p-4px w-50px h-50px bg-white border-1 border-solid border-#1293ff' : 'flex flex-col items-center justify-center rounded-7px p-4px w-50px h-50px bg-white border-1 border-solid border-white'"
|
:class="
|
||||||
|
selectSize === imageSize.key
|
||||||
|
? 'flex flex-col items-center justify-center rounded-7px p-4px w-50px h-50px bg-white border-1 border-solid border-#1293ff'
|
||||||
|
: 'flex flex-col items-center justify-center rounded-7px p-4px w-50px h-50px bg-white border-1 border-solid border-white'
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<div :style="imageSize.style"></div>
|
<div :style="imageSize.style"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -57,7 +61,11 @@
|
||||||
</div>
|
</div>
|
||||||
<el-space wrap class="mt-15px">
|
<el-space wrap class="mt-15px">
|
||||||
<div
|
<div
|
||||||
:class="selectModel === model.key ? 'flex flex-col items-center w-150px overflow-hidden border-3 border-solid border-#1293ff rounded-5px cursor-pointer' : 'flex flex-col items-center w-150px overflow-hidden border-3 border-solid border-transparent cursor-pointer'"
|
:class="
|
||||||
|
selectModel === model.key
|
||||||
|
? 'flex flex-col items-center w-150px overflow-hidden border-3 border-solid border-#1293ff rounded-5px cursor-pointer'
|
||||||
|
: 'flex flex-col items-center w-150px overflow-hidden border-3 border-solid border-transparent cursor-pointer'
|
||||||
|
"
|
||||||
v-for="model in MidjourneyModels"
|
v-for="model in MidjourneyModels"
|
||||||
:key="model.key"
|
:key="model.key"
|
||||||
>
|
>
|
||||||
|
|
@ -71,12 +79,7 @@
|
||||||
<el-text tag="b">版本</el-text>
|
<el-text tag="b">版本</el-text>
|
||||||
</div>
|
</div>
|
||||||
<el-space wrap class="mt-20px w-full">
|
<el-space wrap class="mt-20px w-full">
|
||||||
<el-select
|
<el-select v-model="selectVersion" class="!w-350px" clearable placeholder="请选择版本">
|
||||||
v-model="selectVersion"
|
|
||||||
class="!w-350px"
|
|
||||||
clearable
|
|
||||||
placeholder="请选择版本"
|
|
||||||
>
|
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in versionList"
|
v-for="item in versionList"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
|
|
@ -233,4 +236,3 @@ const settingValues = async (detail: ImageVO) => {
|
||||||
/** 暴露组件方法 */
|
/** 暴露组件方法 */
|
||||||
defineExpose({ settingValues })
|
defineExpose({ settingValues })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -254,4 +254,3 @@ const settingValues = async (detail: ImageVO) => {
|
||||||
/** 暴露组件方法 */
|
/** 暴露组件方法 */
|
||||||
defineExpose({ settingValues })
|
defineExpose({ settingValues })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,19 @@
|
||||||
:suffix-icon="Search"
|
:suffix-icon="Search"
|
||||||
@keyup.enter="handleQuery"
|
@keyup.enter="handleQuery"
|
||||||
/>
|
/>
|
||||||
<div class="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-10px bg-white shadow-[0_0_10px_rgba(0,0,0,0.1)]">
|
<div
|
||||||
|
class="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-10px bg-white shadow-[0_0_10px_rgba(0,0,0,0.1)]"
|
||||||
|
>
|
||||||
<!-- TODO @fan:这个图片的风格,要不和 ImageCard.vue 界面一致?(只有卡片,没有操作);因为看着更有相框的感觉~~~ -->
|
<!-- TODO @fan:这个图片的风格,要不和 ImageCard.vue 界面一致?(只有卡片,没有操作);因为看着更有相框的感觉~~~ -->
|
||||||
<div v-for="item in list" :key="item.id" class="relative overflow-hidden bg-gray-100 cursor-pointer transition-transform duration-300 hover:scale-105">
|
<div
|
||||||
<img :src="item.picUrl" class="w-full h-auto block transition-transform duration-300 hover:scale-110" />
|
v-for="item in list"
|
||||||
|
:key="item.id"
|
||||||
|
class="relative overflow-hidden bg-gray-100 cursor-pointer transition-transform duration-300 hover:scale-105"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="item.picUrl"
|
||||||
|
class="w-full h-auto block transition-transform duration-300 hover:scale-110"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO @fan:缺少翻页 -->
|
<!-- TODO @fan:缺少翻页 -->
|
||||||
|
|
@ -64,4 +73,3 @@ onMounted(async () => {
|
||||||
await getList()
|
await getList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,20 @@
|
||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="modelData.formType === BpmModelFormType.NORMAL" label="流程表单" prop="formId">
|
<el-form-item
|
||||||
|
v-if="modelData.formType === BpmModelFormType.NORMAL"
|
||||||
|
label="流程表单"
|
||||||
|
prop="formId"
|
||||||
|
>
|
||||||
<el-select v-model="modelData.formId" clearable style="width: 100%">
|
<el-select v-model="modelData.formId" clearable style="width: 100%">
|
||||||
<el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
|
<el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="modelData.formType === BpmModelFormType.CUSTOM" label="表单提交路由" prop="formCustomCreatePath">
|
<el-form-item
|
||||||
|
v-if="modelData.formType === BpmModelFormType.CUSTOM"
|
||||||
|
label="表单提交路由"
|
||||||
|
prop="formCustomCreatePath"
|
||||||
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="modelData.formCustomCreatePath"
|
v-model="modelData.formCustomCreatePath"
|
||||||
placeholder="请输入表单提交路由"
|
placeholder="请输入表单提交路由"
|
||||||
|
|
@ -31,7 +39,11 @@
|
||||||
<Icon icon="ep:question" class="ml-5px" />
|
<Icon icon="ep:question" class="ml-5px" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="modelData.formType === BpmModelFormType.CUSTOM" label="表单查看地址" prop="formCustomViewPath">
|
<el-form-item
|
||||||
|
v-if="modelData.formType === BpmModelFormType.CUSTOM"
|
||||||
|
label="表单查看地址"
|
||||||
|
prop="formCustomViewPath"
|
||||||
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="modelData.formCustomViewPath"
|
v-model="modelData.formCustomViewPath"
|
||||||
placeholder="请输入表单查看的组件地址"
|
placeholder="请输入表单查看的组件地址"
|
||||||
|
|
@ -48,7 +60,11 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- 表单预览 -->
|
<!-- 表单预览 -->
|
||||||
<div
|
<div
|
||||||
v-if="modelData.formType === BpmModelFormType.NORMAL && modelData.formId && formPreview.rule.length > 0"
|
v-if="
|
||||||
|
modelData.formType === BpmModelFormType.NORMAL &&
|
||||||
|
modelData.formId &&
|
||||||
|
formPreview.rule.length > 0
|
||||||
|
"
|
||||||
class="mt-20px"
|
class="mt-20px"
|
||||||
>
|
>
|
||||||
<div class="flex items-center mb-15px">
|
<div class="flex items-center mb-15px">
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ onBeforeUnmount(() => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO @unocss 简化 style -->
|
<!-- TODO @unocss 简化 style -->
|
||||||
<div style=" margin: 10px;border: 1px solid #ccc">
|
<div style="margin: 10px; border: 1px solid #ccc">
|
||||||
<Toolbar
|
<Toolbar
|
||||||
style="border-bottom: 1px solid #ccc"
|
style="border-bottom: 1px solid #ccc"
|
||||||
:editor="editorRef"
|
:editor="editorRef"
|
||||||
|
|
@ -106,7 +106,7 @@ onBeforeUnmount(() => {
|
||||||
@insert-mention="insertMention"
|
@insert-mention="insertMention"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div style=" float: right;margin-right: 10px">
|
<div style="float: right; margin-right: 10px">
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,10 @@ function parseHtml(
|
||||||
): SlateElement {
|
): SlateElement {
|
||||||
// TS 语法
|
// TS 语法
|
||||||
|
|
||||||
|
|
||||||
// 生成“流程记录”元素(按照此前约定的数据结构)
|
// 生成“流程记录”元素(按照此前约定的数据结构)
|
||||||
const processRecord = {
|
const processRecord = {
|
||||||
type: 'process-record',
|
type: 'process-record',
|
||||||
children: [{ text: '' }], // void node 必须有 children ,其中有一个空字符串,重要!!!
|
children: [{ text: '' }] // void node 必须有 children ,其中有一个空字符串,重要!!!
|
||||||
}
|
}
|
||||||
|
|
||||||
return processRecord
|
return processRecord
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ onActivated(() => {
|
||||||
.el-form--inline .el-form-item {
|
.el-form--inline .el-form-item {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-divider--horizontal {
|
.el-divider--horizontal {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,7 @@ const submitForm = async () => {
|
||||||
if (!fApi.value || !props.selectProcessDefinition) {
|
if (!fApi.value || !props.selectProcessDefinition) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 流程表单校验
|
// 流程表单校验
|
||||||
await fApi.value.validate()
|
await fApi.value.validate()
|
||||||
|
|
|
||||||
|
|
@ -252,11 +252,7 @@ const flattenAreaTree = (list: AreaNode[] = [], map: Map<string, string> = new M
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapValueWithLabelMap = (
|
const mapValueWithLabelMap = (value: unknown, labelMap: Map<string, string>, separator = ', ') => {
|
||||||
value: unknown,
|
|
||||||
labelMap: Map<string, string>,
|
|
||||||
separator = ', '
|
|
||||||
) => {
|
|
||||||
const values = toValueArray(value)
|
const values = toValueArray(value)
|
||||||
const labels = values
|
const labels = values
|
||||||
.map((item) => escapeHtml(labelMap.get(String(item)) ?? String(item)))
|
.map((item) => escapeHtml(labelMap.get(String(item)) ?? String(item)))
|
||||||
|
|
@ -300,11 +296,7 @@ const loadPrintLookupMaps = async (formFieldsObj: FormFieldRule[]) => {
|
||||||
} satisfies PrintLookupMaps
|
} satisfies PrintLookupMaps
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatPrintField = (
|
const formatPrintField = (rule: FormFieldRule, value: unknown, lookupMaps: PrintLookupMaps) => {
|
||||||
rule: FormFieldRule,
|
|
||||||
value: unknown,
|
|
||||||
lookupMaps: PrintLookupMaps
|
|
||||||
) => {
|
|
||||||
const type = String(rule.type ?? '')
|
const type = String(rule.type ?? '')
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
@ -356,7 +348,9 @@ const formatPrintField = (
|
||||||
return renderFileListHtml(value)
|
return renderFileListHtml(value)
|
||||||
case 'IframeComponent': {
|
case 'IframeComponent': {
|
||||||
const propsObj = rule.props
|
const propsObj = rule.props
|
||||||
const propsUrl = isPrintableRecord(propsObj) ? String(getRecordValue(propsObj, 'url') ?? '') : ''
|
const propsUrl = isPrintableRecord(propsObj)
|
||||||
|
? String(getRecordValue(propsObj, 'url') ?? '')
|
||||||
|
: ''
|
||||||
const iframeUrl = isEmptyValue(value) ? propsUrl : String(value ?? '')
|
const iframeUrl = isEmptyValue(value) ? propsUrl : String(value ?? '')
|
||||||
return iframeUrl ? createFileLinkHtml(iframeUrl) : ''
|
return iframeUrl ? createFileLinkHtml(iframeUrl) : ''
|
||||||
}
|
}
|
||||||
|
|
@ -395,10 +389,10 @@ const initPrintDataMap = () => {
|
||||||
printDataMap.value['processNum'] = String(printData.value.processInstance.id ?? '')
|
printDataMap.value['processNum'] = String(printData.value.processInstance.id ?? '')
|
||||||
printDataMap.value['startTime'] = formatDate(printData.value.processInstance.startTime)
|
printDataMap.value['startTime'] = formatDate(printData.value.processInstance.startTime)
|
||||||
printDataMap.value['endTime'] = formatDate(printData.value.processInstance.endTime)
|
printDataMap.value['endTime'] = formatDate(printData.value.processInstance.endTime)
|
||||||
printDataMap.value['processStatus'] = String(getDictLabel(
|
printDataMap.value['processStatus'] = String(
|
||||||
DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS,
|
getDictLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, printData.value.processInstance.status) ??
|
||||||
printData.value.processInstance.status
|
''
|
||||||
) ?? '')
|
)
|
||||||
printDataMap.value['printUser'] = userName.value
|
printDataMap.value['printUser'] = userName.value
|
||||||
printDataMap.value['printTime'] = printTime.value
|
printDataMap.value['printTime'] = printTime.value
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,12 @@
|
||||||
{{ formatPast2(scope.row.durationInMillis) }}
|
{{ formatPast2(scope.row.durationInMillis) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="流程编号" prop="processInstanceId" :show-overflow-tooltip="true" />
|
<el-table-column
|
||||||
|
align="center"
|
||||||
|
label="流程编号"
|
||||||
|
prop="processInstanceId"
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
/>
|
||||||
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
||||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,7 @@ const getList = async () => {
|
||||||
const currentUserId = getCurrentUserId()
|
const currentUserId = getCurrentUserId()
|
||||||
const permission = list.value.find(
|
const permission = list.value.find(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.userId === currentUserId &&
|
item.userId === currentUserId && item.level === PermissionApi.PermissionLevelEnum.OWNER
|
||||||
item.level === PermissionApi.PermissionLevelEnum.OWNER
|
|
||||||
)
|
)
|
||||||
if (permission) {
|
if (permission) {
|
||||||
formData.value.ownerUserId = currentUserId
|
formData.value.ownerUserId = currentUserId
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
node-key="id"
|
node-key="id"
|
||||||
placeholder="请选择归属部门"
|
placeholder="请选择归属部门"
|
||||||
@change="(queryParams.userId = undefined), handleQuery()"
|
@change="((queryParams.userId = undefined), handleQuery())"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="员工" prop="userId">
|
<el-form-item label="员工" prop="userId">
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
node-key="id"
|
node-key="id"
|
||||||
placeholder="请选择归属部门"
|
placeholder="请选择归属部门"
|
||||||
@change="(queryParams.userId = undefined), handleQuery()"
|
@change="((queryParams.userId = undefined), handleQuery())"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="员工" prop="userId">
|
<el-form-item label="员工" prop="userId">
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,7 @@
|
||||||
:index="idx"
|
:index="idx"
|
||||||
:key="resolveItemKey(item, idx)"
|
:key="resolveItemKey(item, idx)"
|
||||||
></slot>
|
></slot>
|
||||||
<div
|
<div v-if="showFooter" class="py-3 text-xs text-center text-[var(--el-text-color-secondary)]">
|
||||||
v-if="showFooter"
|
|
||||||
class="py-3 text-xs text-center text-[var(--el-text-color-secondary)]"
|
|
||||||
>
|
|
||||||
已到底部
|
已到底部
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@
|
||||||
title="拖拽调整宽度"
|
title="拖拽调整宽度"
|
||||||
@mousedown="startResize"
|
@mousedown="startResize"
|
||||||
>
|
>
|
||||||
<div class="im-resizable-aside__line w-0.5 h-full rounded-0.5 bg-transparent transition-all"></div>
|
<div
|
||||||
|
class="im-resizable-aside__line w-0.5 h-full rounded-0.5 bg-transparent transition-all"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,7 @@ import { computed } from 'vue'
|
||||||
|
|
||||||
import UserAvatar from '../user/UserAvatar.vue'
|
import UserAvatar from '../user/UserAvatar.vue'
|
||||||
import { isPrivateConversation } from '@/views/im/utils/constants'
|
import { isPrivateConversation } from '@/views/im/utils/constants'
|
||||||
import {
|
import { getCardLabelInfo, type CardMessage, type CardTarget } from '@/views/im/utils/message'
|
||||||
getCardLabelInfo,
|
|
||||||
type CardMessage,
|
|
||||||
type CardTarget
|
|
||||||
} from '@/views/im/utils/message'
|
|
||||||
|
|
||||||
defineOptions({ name: 'ImCardBubble' })
|
defineOptions({ name: 'ImCardBubble' })
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,9 +168,7 @@ const message = useMessage()
|
||||||
const currentUserId = computed(() => getCurrentUserId())
|
const currentUserId = computed(() => getCurrentUserId())
|
||||||
|
|
||||||
/** 搜索结果过滤掉自己;用 v-if 而非 v-show,避免 DOM 占位 + 头像无效请求 */
|
/** 搜索结果过滤掉自己;用 v-if 而非 v-show,避免 DOM 占位 + 头像无效请求 */
|
||||||
const visibleUsers = computed(() =>
|
const visibleUsers = computed(() => users.value.filter((user) => user.id !== currentUserId.value))
|
||||||
users.value.filter((user) => user.id !== currentUserId.value)
|
|
||||||
)
|
|
||||||
const keyword = ref('')
|
const keyword = ref('')
|
||||||
const users = ref<UserVO[]>([])
|
const users = ref<UserVO[]>([])
|
||||||
const searched = ref(false)
|
const searched = ref(false)
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,7 @@ import { computed, ref, watch } from 'vue'
|
||||||
import UserAvatar from '../user/UserAvatar.vue'
|
import UserAvatar from '../user/UserAvatar.vue'
|
||||||
import { useFriendStore } from '../../store/friendStore'
|
import { useFriendStore } from '../../store/friendStore'
|
||||||
import { useGroupStore } from '../../store/groupStore'
|
import { useGroupStore } from '../../store/groupStore'
|
||||||
import {
|
import { buildGroupAvatar, getCachedGroupAvatar, setCachedGroupAvatar } from '../../../utils/group'
|
||||||
buildGroupAvatar,
|
|
||||||
getCachedGroupAvatar,
|
|
||||||
setCachedGroupAvatar
|
|
||||||
} from '../../../utils/group'
|
|
||||||
import { getMemberDisplayName } from '../../../utils/user'
|
import { getMemberDisplayName } from '../../../utils/user'
|
||||||
import type { GroupMember } from '../../types'
|
import type { GroupMember } from '../../types'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,7 @@ onUnmounted(() => window.removeEventListener('keydown', handleKeydown))
|
||||||
function handleChat(group: GroupLite) {
|
function handleChat(group: GroupLite) {
|
||||||
const cached = groupStore.getGroup(group.id)
|
const cached = groupStore.getGroup(group.id)
|
||||||
// cached 命中走 getGroupDisplayName 让群备注优先(与 contact / 会话列表的展示名一致);缺 cached 时回落 showGroupName / 原群名
|
// cached 命中走 getGroupDisplayName 让群备注优先(与 contact / 会话列表的展示名一致);缺 cached 时回落 showGroupName / 原群名
|
||||||
const displayName = cached
|
const displayName = cached ? getGroupDisplayName(cached) : group.showGroupName || group.name || ''
|
||||||
? getGroupDisplayName(cached)
|
|
||||||
: group.showGroupName || group.name || ''
|
|
||||||
// 打开或新建会话
|
// 打开或新建会话
|
||||||
conversationStore.openConversation(
|
conversationStore.openConversation(
|
||||||
group.id,
|
group.id,
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,9 @@
|
||||||
<el-dialog v-model="visible" title="设置禁言" width="560px" :close-on-click-modal="false">
|
<el-dialog v-model="visible" title="设置禁言" width="560px" :close-on-click-modal="false">
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<!-- 成员信息卡:和 FriendAddDialog 的 user 卡保持一致的浅色背景 -->
|
<!-- 成员信息卡:和 FriendAddDialog 的 user 卡保持一致的浅色背景 -->
|
||||||
<div
|
<div class="flex items-center gap-2 px-3 py-2.5 rounded-md bg-[var(--el-fill-color-light)]">
|
||||||
class="flex items-center gap-2 px-3 py-2.5 rounded-md bg-[var(--el-fill-color-light)]"
|
|
||||||
>
|
|
||||||
<span class="text-13px text-[var(--el-text-color-secondary)]">禁言成员</span>
|
<span class="text-13px text-[var(--el-text-color-secondary)]">禁言成员</span>
|
||||||
<span
|
<span class="text-sm font-medium text-[var(--el-text-color-primary)] truncate">
|
||||||
class="text-sm font-medium text-[var(--el-text-color-primary)] truncate"
|
|
||||||
>
|
|
||||||
{{ memberName }}
|
{{ memberName }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -313,22 +313,23 @@ function updateLocalResult(id: number, handleResult: number) {
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 自绘按钮:贴近微信小药丸样式;与 :disabled、:hover:not(:disabled) 等伪类叠加 modifier 类的组合选择器写在 class 里成本高,留 SCSS */
|
/* 自绘按钮:贴近微信小药丸样式;与 :disabled、:hover:not(:disabled) 等伪类叠加 modifier 类的组合选择器写在 class 里成本高,留 SCSS */
|
||||||
.im-group-request-list__btn {
|
.im-group-request-list__btn {
|
||||||
flex-shrink: 0;
|
|
||||||
min-width: 56px;
|
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
min-width: 56px;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
border-radius: 4px;
|
||||||
transition:
|
transition:
|
||||||
background-color 0.15s,
|
background-color 0.15s,
|
||||||
border-color 0.15s,
|
border-color 0.15s,
|
||||||
color 0.15s;
|
color 0.15s;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-group-request-list__btn:disabled {
|
.im-group-request-list__btn:disabled {
|
||||||
opacity: 0.6;
|
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-group-request-list__btn--primary {
|
.im-group-request-list__btn--primary {
|
||||||
|
|
@ -336,6 +337,7 @@ function updateLocalResult(id: number, handleResult: number) {
|
||||||
background-color: var(--el-color-primary);
|
background-color: var(--el-color-primary);
|
||||||
border-color: var(--el-color-primary);
|
border-color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-group-request-list__btn--primary:hover:not(:disabled) {
|
.im-group-request-list__btn--primary:hover:not(:disabled) {
|
||||||
background-color: var(--el-color-primary-light-3);
|
background-color: var(--el-color-primary-light-3);
|
||||||
border-color: var(--el-color-primary-light-3);
|
border-color: var(--el-color-primary-light-3);
|
||||||
|
|
@ -346,6 +348,7 @@ function updateLocalResult(id: number, handleResult: number) {
|
||||||
background-color: var(--el-bg-color);
|
background-color: var(--el-bg-color);
|
||||||
border-color: var(--el-border-color);
|
border-color: var(--el-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-group-request-list__btn--ghost:hover:not(:disabled) {
|
.im-group-request-list__btn--ghost:hover:not(:disabled) {
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
border-color: var(--el-color-primary);
|
border-color: var(--el-color-primary);
|
||||||
|
|
|
||||||
|
|
@ -363,6 +363,7 @@ function handleToggle(conversation: Conversation) {
|
||||||
.im-conversation-picker__recent::-webkit-scrollbar {
|
.im-conversation-picker__recent::-webkit-scrollbar {
|
||||||
height: 4px;
|
height: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-conversation-picker__recent::-webkit-scrollbar-thumb {
|
.im-conversation-picker__recent::-webkit-scrollbar-thumb {
|
||||||
background-color: var(--el-border-color);
|
background-color: var(--el-border-color);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
|
||||||
|
|
@ -181,9 +181,7 @@ const disabledSet = computed(() => new Set(props.disabledIds))
|
||||||
const selectedSet = computed(() => new Set(props.selectedIds))
|
const selectedSet = computed(() => new Set(props.selectedIds))
|
||||||
|
|
||||||
/** 候选好友:剔除 hideIds(hide 优先级最高) */
|
/** 候选好友:剔除 hideIds(hide 优先级最高) */
|
||||||
const candidates = computed(() =>
|
const candidates = computed(() => props.friends.filter((friend) => !hideSet.value.has(friend.id)))
|
||||||
props.friends.filter((friend) => !hideSet.value.has(friend.id))
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 委托 useFriendBuckets:搜索规则复用,左侧列表按滚动分页渲染 */
|
/** 委托 useFriendBuckets:搜索规则复用,左侧列表按滚动分页渲染 */
|
||||||
const { filtered } = useFriendBuckets(candidates, keyword)
|
const { filtered } = useFriendBuckets(candidates, keyword)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
<PagedScroller :items="shownMembers" :page-size="30" item-key="userId">
|
<PagedScroller :items="shownMembers" :page-size="30" item-key="userId">
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<GroupMemberItem
|
<GroupMemberItem
|
||||||
:member="(item as GroupMemberLite)"
|
:member="item as GroupMemberLite"
|
||||||
:height="46"
|
:height="46"
|
||||||
@click="handleToggle(item as GroupMemberLite)"
|
@click="handleToggle(item as GroupMemberLite)"
|
||||||
>
|
>
|
||||||
|
|
@ -86,11 +86,7 @@
|
||||||
|
|
||||||
<!-- grid 形态:宫格预览;非 locked 成员右上角叠加 × 移除(locked 不渲染) -->
|
<!-- grid 形态:宫格预览;非 locked 成员右上角叠加 × 移除(locked 不渲染) -->
|
||||||
<div v-else class="flex flex-wrap p-2.5">
|
<div v-else class="flex flex-wrap p-2.5">
|
||||||
<GroupMemberGrid
|
<GroupMemberGrid v-for="member in selectedMembers" :key="member.userId" :member="member">
|
||||||
v-for="member in selectedMembers"
|
|
||||||
:key="member.userId"
|
|
||||||
:member="member"
|
|
||||||
>
|
|
||||||
<Icon
|
<Icon
|
||||||
v-if="!isLocked(member)"
|
v-if="!isLocked(member)"
|
||||||
icon="ant-design:close-circle-filled"
|
icon="ant-design:close-circle-filled"
|
||||||
|
|
@ -240,4 +236,3 @@ function handleToggle(member: GroupMemberLite) {
|
||||||
emit('update:selectedIds', next)
|
emit('update:selectedIds', next)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,11 +71,7 @@ import {
|
||||||
inviteCall,
|
inviteCall,
|
||||||
noAnswerCallCheck
|
noAnswerCallCheck
|
||||||
} from '@/api/im/rtc'
|
} from '@/api/im/rtc'
|
||||||
import {
|
import { ImRtcCallMediaType, ImRtcCallStage, ImConversationType } from '@/views/im/utils/constants'
|
||||||
ImRtcCallMediaType,
|
|
||||||
ImRtcCallStage,
|
|
||||||
ImConversationType
|
|
||||||
} from '@/views/im/utils/constants'
|
|
||||||
import { RTC_NO_ANSWER_CALL_CHECK_INTERVAL_MS } from '@/views/im/utils/config'
|
import { RTC_NO_ANSWER_CALL_CHECK_INTERVAL_MS } from '@/views/im/utils/config'
|
||||||
import { getCurrentUserId } from '@/utils/auth'
|
import { getCurrentUserId } from '@/utils/auth'
|
||||||
import { getSenderAvatar, getSenderDisplayName } from '@/views/im/utils/user'
|
import { getSenderAvatar, getSenderDisplayName } from '@/views/im/utils/user'
|
||||||
|
|
@ -104,17 +100,15 @@ const hangingUp = ref(false)
|
||||||
/** 当前是否视频通话 */
|
/** 当前是否视频通话 */
|
||||||
const isVideo = computed(() => {
|
const isVideo = computed(() => {
|
||||||
const t =
|
const t =
|
||||||
rtcStore.call?.mediaType ||
|
rtcStore.call?.mediaType || rtcStore.incomingPayload?.mediaType || ImRtcCallMediaType.VOICE
|
||||||
rtcStore.incomingPayload?.mediaType ||
|
|
||||||
ImRtcCallMediaType.VOICE
|
|
||||||
return t === ImRtcCallMediaType.VIDEO
|
return t === ImRtcCallMediaType.VIDEO
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 当前是否群通话;决定浮动窗大小 */
|
/** 当前是否群通话;决定浮动窗大小 */
|
||||||
const isGroup = computed(
|
const isGroup = computed(
|
||||||
() =>
|
() =>
|
||||||
(rtcStore.call?.conversationType ??
|
(rtcStore.call?.conversationType ?? rtcStore.incomingPayload?.conversationType) ===
|
||||||
rtcStore.incomingPayload?.conversationType) === ImConversationType.GROUP
|
ImConversationType.GROUP
|
||||||
)
|
)
|
||||||
|
|
||||||
/** 初始摄像头是否打开;群通话默认全部关闭,进入后用户主动开 */
|
/** 初始摄像头是否打开;群通话默认全部关闭,进入后用户主动开 */
|
||||||
|
|
@ -261,11 +255,7 @@ function maybeEnterRunning() {
|
||||||
watch(
|
watch(
|
||||||
() => rtcStore.stage,
|
() => rtcStore.stage,
|
||||||
async (stage) => {
|
async (stage) => {
|
||||||
if (
|
if (stage === ImRtcCallStage.INVITING && rtcStore.call?.token && rtcStore.call?.livekitUrl) {
|
||||||
stage === ImRtcCallStage.INVITING &&
|
|
||||||
rtcStore.call?.token &&
|
|
||||||
rtcStore.call?.livekitUrl
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
await connectLiveKit(rtcStore.call.livekitUrl, rtcStore.call.token)
|
await connectLiveKit(rtcStore.call.livekitUrl, rtcStore.call.token)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -417,7 +407,9 @@ function handlePeerDisconnected() {
|
||||||
|
|
||||||
/** 通话存活期间(INVITING / INCOMING / RUNNING)周期性触发后端扫该 room 的超时 INVITING;保持 timer 是为了 inviteCall 追加新人后也能覆盖;阈值由后端配置决定,前端只负责 trigger */
|
/** 通话存活期间(INVITING / INCOMING / RUNNING)周期性触发后端扫该 room 的超时 INVITING;保持 timer 是为了 inviteCall 追加新人后也能覆盖;阈值由后端配置决定,前端只负责 trigger */
|
||||||
const { resume: resumeNoAnswerTimer, pause: pauseNoAnswerTimer } = useIntervalFn(
|
const { resume: resumeNoAnswerTimer, pause: pauseNoAnswerTimer } = useIntervalFn(
|
||||||
triggerNoAnswerCallCheck, RTC_NO_ANSWER_CALL_CHECK_INTERVAL_MS, { immediate: false }
|
triggerNoAnswerCallCheck,
|
||||||
|
RTC_NO_ANSWER_CALL_CHECK_INTERVAL_MS,
|
||||||
|
{ immediate: false }
|
||||||
)
|
)
|
||||||
watch(
|
watch(
|
||||||
() => rtcStore.isActive,
|
() => rtcStore.isActive,
|
||||||
|
|
|
||||||
|
|
@ -97,12 +97,14 @@ const audioRef = useMediaStreamElement<HTMLAudioElement>(() => props.participant
|
||||||
.tile-dot {
|
.tile-dot {
|
||||||
animation: tile-dot 1.4s infinite ease-in-out both;
|
animation: tile-dot 1.4s infinite ease-in-out both;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes tile-dot {
|
@keyframes tile-dot {
|
||||||
0%,
|
0%,
|
||||||
80%,
|
80%,
|
||||||
100% {
|
100% {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
40% {
|
40% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,9 +85,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 底部操作区:麦克风 / 扬声器 / 摄像头 / (群聊:共享屏幕 / 添加成员) / 挂断 -->
|
<!-- 底部操作区:麦克风 / 扬声器 / 摄像头 / (群聊:共享屏幕 / 添加成员) / 挂断 -->
|
||||||
<div
|
<div class="flex flex-shrink-0 gap-3 justify-around items-center pt-4 px-4 pb-5 bg-black/20">
|
||||||
class="flex flex-shrink-0 gap-3 justify-around items-center pt-4 px-4 pb-5 bg-black/20"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-col gap-2 items-center cursor-pointer select-none min-w-[64px]"
|
class="flex flex-col gap-2 items-center cursor-pointer select-none min-w-[64px]"
|
||||||
@click="$emit('toggle-mic')"
|
@click="$emit('toggle-mic')"
|
||||||
|
|
@ -164,7 +162,9 @@
|
||||||
class="flex flex-col gap-2 items-center cursor-pointer select-none min-w-[64px]"
|
class="flex flex-col gap-2 items-center cursor-pointer select-none min-w-[64px]"
|
||||||
@click="$emit('add-member')"
|
@click="$emit('add-member')"
|
||||||
>
|
>
|
||||||
<span class="flex justify-center items-center w-[52px] h-[52px] text-white rounded-full bg-white/15">
|
<span
|
||||||
|
class="flex justify-center items-center w-[52px] h-[52px] text-white rounded-full bg-white/15"
|
||||||
|
>
|
||||||
<Icon icon="ant-design:plus-outlined" :size="22" />
|
<Icon icon="ant-design:plus-outlined" :size="22" />
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs text-white/70 whitespace-nowrap">添加成员</span>
|
<span class="text-xs text-white/70 whitespace-nowrap">添加成员</span>
|
||||||
|
|
@ -175,7 +175,9 @@
|
||||||
:class="{ 'opacity-60 pointer-events-none': hangingUp }"
|
:class="{ 'opacity-60 pointer-events-none': hangingUp }"
|
||||||
@click="$emit('hangup')"
|
@click="$emit('hangup')"
|
||||||
>
|
>
|
||||||
<span class="flex justify-center items-center w-[52px] h-[52px] text-white rounded-full bg-[#f04a4a]">
|
<span
|
||||||
|
class="flex justify-center items-center w-[52px] h-[52px] text-white rounded-full bg-[#f04a4a]"
|
||||||
|
>
|
||||||
<Icon icon="ant-design:phone-outlined" :size="22" class="rotate-[135deg]" />
|
<Icon icon="ant-design:phone-outlined" :size="22" class="rotate-[135deg]" />
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs text-white/70 whitespace-nowrap">挂断</span>
|
<span class="text-xs text-white/70 whitespace-nowrap">挂断</span>
|
||||||
|
|
@ -277,11 +279,13 @@ const formattedDuration = computed(() =>
|
||||||
.reconnect-dot {
|
.reconnect-dot {
|
||||||
animation: reconnect-pulse 1s ease-in-out infinite;
|
animation: reconnect-pulse 1s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes reconnect-pulse {
|
@keyframes reconnect-pulse {
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,9 @@ async function handleSend() {
|
||||||
const results = await Promise.all(tasks)
|
const results = await Promise.all(tasks)
|
||||||
const failedNames = results.filter((r) => !r.ok).map((r) => r.conversation.name || '未命名会话')
|
const failedNames = results.filter((r) => !r.ok).map((r) => r.conversation.name || '未命名会话')
|
||||||
// 把命中的目标推到最近转发列表(部分失败也推:用户的"意图"已表达)
|
// 把命中的目标推到最近转发列表(部分失败也推:用户的"意图"已表达)
|
||||||
conversationStore.pushRecentForwardConversationKeyList(targets.map((c) => getConversationKey(c)))
|
conversationStore.pushRecentForwardConversationKeyList(
|
||||||
|
targets.map((c) => getConversationKey(c))
|
||||||
|
)
|
||||||
if (failedNames.length === 0) {
|
if (failedNames.length === 0) {
|
||||||
message.success('已转发')
|
message.success('已转发')
|
||||||
} else if (failedNames.length === targets.length) {
|
} else if (failedNames.length === targets.length) {
|
||||||
|
|
|
||||||
|
|
@ -101,9 +101,7 @@ function handleSendMessage() {
|
||||||
}
|
}
|
||||||
// 取 friendStore 里的最新备注 / 免打扰,避免新建会话用过期数据
|
// 取 friendStore 里的最新备注 / 免打扰,避免新建会话用过期数据
|
||||||
const friend = friendStore.getFriend(user.value.id)
|
const friend = friendStore.getFriend(user.value.id)
|
||||||
const conversationName = friend
|
const conversationName = friend ? getFriendDisplayName(friend) : user.value.nickname || ''
|
||||||
? getFriendDisplayName(friend)
|
|
||||||
: user.value.nickname || ''
|
|
||||||
conversationStore.openConversation(
|
conversationStore.openConversation(
|
||||||
user.value.id,
|
user.value.id,
|
||||||
ImConversationType.PRIVATE,
|
ImConversationType.PRIVATE,
|
||||||
|
|
|
||||||
|
|
@ -81,12 +81,12 @@ export function useLiveKitRoom() {
|
||||||
_room.value = r
|
_room.value = r
|
||||||
|
|
||||||
r.on(RoomEvent.ParticipantConnected, (rp) => {
|
r.on(RoomEvent.ParticipantConnected, (rp) => {
|
||||||
syncRemotes(r)
|
syncRemotes(r)
|
||||||
const userId = parseUserId(rp.identity)
|
const userId = parseUserId(rp.identity)
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
participantConnectedHandlers.forEach((cb) => cb(userId))
|
participantConnectedHandlers.forEach((cb) => cb(userId))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on(RoomEvent.ParticipantDisconnected, (rp) => {
|
.on(RoomEvent.ParticipantDisconnected, (rp) => {
|
||||||
syncRemotes(r)
|
syncRemotes(r)
|
||||||
// 离开的参与者缓存清掉,避免下次同 sid 重连命中失效引用
|
// 离开的参与者缓存清掉,避免下次同 sid 重连命中失效引用
|
||||||
|
|
|
||||||
|
|
@ -198,15 +198,17 @@ export const useMediaUploader = () => {
|
||||||
uploadProgress: 0,
|
uploadProgress: 0,
|
||||||
_localFile: opts.file
|
_localFile: opts.file
|
||||||
}
|
}
|
||||||
void messageStore.insertMessage(
|
void messageStore
|
||||||
{
|
.insertMessage(
|
||||||
type: conversation.type,
|
{
|
||||||
targetId: conversation.targetId,
|
type: conversation.type,
|
||||||
name: conversation.name || String(conversation.targetId),
|
targetId: conversation.targetId,
|
||||||
avatar: conversation.avatar || ''
|
name: conversation.name || String(conversation.targetId),
|
||||||
},
|
avatar: conversation.avatar || ''
|
||||||
placeholder
|
},
|
||||||
).catch(() => undefined)
|
placeholder
|
||||||
|
)
|
||||||
|
.catch(() => undefined)
|
||||||
return { clientMessageId, blobUrl }
|
return { clientMessageId, blobUrl }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,14 +113,10 @@ const currentUserId = computed(() => getCurrentUserId())
|
||||||
const iSentIt = computed(() => props.request.fromUserId === currentUserId.value)
|
const iSentIt = computed(() => props.request.fromUserId === currentUserId.value)
|
||||||
|
|
||||||
/** 是否「已拒绝」态:模板里多处用到,computed 一次省得到处写枚举比对 */
|
/** 是否「已拒绝」态:模板里多处用到,computed 一次省得到处写枚举比对 */
|
||||||
const refused = computed(
|
const refused = computed(() => props.request.handleResult === ImFriendRequestHandleResult.REFUSED)
|
||||||
() => props.request.handleResult === ImFriendRequestHandleResult.REFUSED
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 是否「已通过」态:转走 UserInfo 好友详情入口 */
|
/** 是否「已通过」态:转走 UserInfo 好友详情入口 */
|
||||||
const agreed = computed(
|
const agreed = computed(() => props.request.handleResult === ImFriendRequestHandleResult.AGREED)
|
||||||
() => props.request.handleResult === ImFriendRequestHandleResult.AGREED
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 对端的用户编号 / 昵称 / 头像 */
|
/** 对端的用户编号 / 昵称 / 头像 */
|
||||||
const peerUserId = computed(() =>
|
const peerUserId = computed(() =>
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,15 @@
|
||||||
title="邀请好友入群"
|
title="邀请好友入群"
|
||||||
@click="handleOpenInvite"
|
@click="handleOpenInvite"
|
||||||
>
|
>
|
||||||
<div class="im-conversation-group-side__icon-tile flex items-center justify-center w-[50px] h-[50px] text-20px text-[var(--el-text-color-regular)] bg-[var(--el-fill-color-lighter)] border border-dashed border-[var(--el-border-color)] rounded-md transition-colors duration-200">
|
<div
|
||||||
|
class="im-conversation-group-side__icon-tile flex items-center justify-center w-[50px] h-[50px] text-20px text-[var(--el-text-color-regular)] bg-[var(--el-fill-color-lighter)] border border-dashed border-[var(--el-border-color)] rounded-md transition-colors duration-200"
|
||||||
|
>
|
||||||
<Icon icon="ant-design:plus-outlined" />
|
<Icon icon="ant-design:plus-outlined" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1.5 text-12px leading-[1.5] text-[var(--el-text-color-regular)] text-center">添加</div>
|
<div
|
||||||
|
class="mt-1.5 text-12px leading-[1.5] text-[var(--el-text-color-regular)] text-center"
|
||||||
|
>添加</div
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 移出(群主或管理员;管理员只能移出普通成员,由后端校验) -->
|
<!-- 移出(群主或管理员;管理员只能移出普通成员,由后端校验) -->
|
||||||
|
|
@ -52,10 +57,15 @@
|
||||||
title="移出群成员"
|
title="移出群成员"
|
||||||
@click="handleOpenRemove"
|
@click="handleOpenRemove"
|
||||||
>
|
>
|
||||||
<div class="im-conversation-group-side__icon-tile flex items-center justify-center w-[50px] h-[50px] text-20px text-[var(--el-text-color-regular)] bg-[var(--el-fill-color-lighter)] border border-dashed border-[var(--el-border-color)] rounded-md transition-colors duration-200">
|
<div
|
||||||
|
class="im-conversation-group-side__icon-tile flex items-center justify-center w-[50px] h-[50px] text-20px text-[var(--el-text-color-regular)] bg-[var(--el-fill-color-lighter)] border border-dashed border-[var(--el-border-color)] rounded-md transition-colors duration-200"
|
||||||
|
>
|
||||||
<Icon icon="ant-design:minus-outlined" />
|
<Icon icon="ant-design:minus-outlined" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1.5 text-12px leading-[1.5] text-[var(--el-text-color-regular)] text-center">移出</div>
|
<div
|
||||||
|
class="mt-1.5 text-12px leading-[1.5] text-[var(--el-text-color-regular)] text-center"
|
||||||
|
>移出</div
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -87,8 +97,13 @@
|
||||||
<div
|
<div
|
||||||
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">群聊名称</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
<span class="text-13px text-[var(--el-text-color-regular)] break-all leading-[1.6] truncate">{{ group.name }}</span>
|
>群聊名称</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="text-13px text-[var(--el-text-color-regular)] break-all leading-[1.6] truncate"
|
||||||
|
>{{ group.name }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
|
|
@ -103,8 +118,13 @@
|
||||||
v-else
|
v-else
|
||||||
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 transition-colors duration-150"
|
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 transition-colors duration-150"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">群聊名称</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
<span class="text-13px text-[var(--el-text-color-regular)] break-all leading-[1.6] truncate">{{ group.name }}</span>
|
>群聊名称</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="text-13px text-[var(--el-text-color-regular)] break-all leading-[1.6] truncate"
|
||||||
|
>{{ group.name }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 群公告(群主可改):内容可能很长,加 > chevron 表示可展开编辑 -->
|
<!-- 群公告(群主可改):内容可能很长,加 > chevron 表示可展开编辑 -->
|
||||||
|
|
@ -120,7 +140,9 @@
|
||||||
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between gap-2">
|
<div class="flex items-center justify-between gap-2">
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">群公告</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>群公告</span
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:right-outlined"
|
icon="ant-design:right-outlined"
|
||||||
:size="11"
|
:size="11"
|
||||||
|
|
@ -133,7 +155,9 @@
|
||||||
>
|
>
|
||||||
{{ group.notice }}
|
{{ group.notice }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-13px text-[var(--el-text-color-placeholder)] leading-[1.6]">未设置</span>
|
<span v-else class="text-13px text-[var(--el-text-color-placeholder)] leading-[1.6]"
|
||||||
|
>未设置</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
|
|
@ -162,7 +186,9 @@
|
||||||
>
|
>
|
||||||
{{ group.notice }}
|
{{ group.notice }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-13px text-[var(--el-text-color-placeholder)] leading-[1.6]">未设置</span>
|
<span v-else class="text-13px text-[var(--el-text-color-placeholder)] leading-[1.6]"
|
||||||
|
>未设置</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 备注(仅自己可见;保存后会替换会话列表 / 顶部群名展示);历史退群群隐藏:改备注走 updateGroupMember,已退群会被后端拒 -->
|
<!-- 备注(仅自己可见;保存后会替换会话列表 / 顶部群名展示);历史退群群隐藏:改备注走 updateGroupMember,已退群会被后端拒 -->
|
||||||
|
|
@ -177,14 +203,19 @@
|
||||||
<div
|
<div
|
||||||
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">备注</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>备注</span
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
v-if="group.groupRemark"
|
v-if="group.groupRemark"
|
||||||
class="text-13px text-[var(--el-text-color-regular)] break-all leading-[1.6] line-clamp-2"
|
class="text-13px text-[var(--el-text-color-regular)] break-all leading-[1.6] line-clamp-2"
|
||||||
>
|
>
|
||||||
{{ group.groupRemark }}
|
{{ group.groupRemark }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-13px text-[var(--el-text-color-placeholder)] leading-[1.6]">
|
<span
|
||||||
|
v-else
|
||||||
|
class="text-13px text-[var(--el-text-color-placeholder)] leading-[1.6]"
|
||||||
|
>
|
||||||
群聊的备注仅自己可见
|
群聊的备注仅自己可见
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -217,14 +248,18 @@
|
||||||
<div
|
<div
|
||||||
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-group-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">我在本群的昵称</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>我在本群的昵称</span
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
v-if="group.remarkNickName"
|
v-if="group.remarkNickName"
|
||||||
class="text-13px text-[var(--el-text-color-regular)] break-all leading-[1.6] truncate"
|
class="text-13px text-[var(--el-text-color-regular)] break-all leading-[1.6] truncate"
|
||||||
>
|
>
|
||||||
{{ group.remarkNickName }}
|
{{ group.remarkNickName }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-13px text-[var(--el-text-color-placeholder)] leading-[1.6]">点击设置</span>
|
<span v-else class="text-13px text-[var(--el-text-color-placeholder)] leading-[1.6]"
|
||||||
|
>点击设置</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
|
|
@ -249,7 +284,9 @@
|
||||||
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
@click="emit('open-history')"
|
@click="emit('open-history')"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">查找聊天内容</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>查找聊天内容</span
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:right-outlined"
|
icon="ant-design:right-outlined"
|
||||||
:size="11"
|
:size="11"
|
||||||
|
|
@ -262,7 +299,9 @@
|
||||||
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
@click="handleShareGroupCard"
|
@click="handleShareGroupCard"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">分享群名片</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>分享群名片</span
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:right-outlined"
|
icon="ant-design:right-outlined"
|
||||||
:size="11"
|
:size="11"
|
||||||
|
|
@ -275,17 +314,30 @@
|
||||||
|
|
||||||
<!-- ==================== 开关项 ==================== -->
|
<!-- ==================== 开关项 ==================== -->
|
||||||
<div class="bg-[var(--el-bg-color)]">
|
<div class="bg-[var(--el-bg-color)]">
|
||||||
<div class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
|
<div
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">消息免打扰</span>
|
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>消息免打扰</span
|
||||||
|
>
|
||||||
<el-switch :model-value="!!conversation?.silent" @change="onMutedChange" />
|
<el-switch :model-value="!!conversation?.silent" @change="onMutedChange" />
|
||||||
</div>
|
</div>
|
||||||
<div class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
|
<div
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">置顶聊天</span>
|
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>置顶聊天</span
|
||||||
|
>
|
||||||
<el-switch :model-value="!!conversation?.top" @change="onTopChange" />
|
<el-switch :model-value="!!conversation?.top" @change="onTopChange" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 全群禁言:仅群主或管理员可操作 -->
|
<!-- 全群禁言:仅群主或管理员可操作 -->
|
||||||
<div v-if="isOwnerOrAdmin" class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
|
<div
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">全群禁言</span>
|
v-if="isOwnerOrAdmin"
|
||||||
|
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>全群禁言</span
|
||||||
|
>
|
||||||
<el-switch :model-value="!!currentMutedAll" @change="onMuteAllChange" />
|
<el-switch :model-value="!!currentMutedAll" @change="onMuteAllChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -296,8 +348,13 @@
|
||||||
<div class="flex-shrink-0 h-[10px]"></div>
|
<div class="flex-shrink-0 h-[10px]"></div>
|
||||||
<div class="bg-[var(--el-bg-color)]">
|
<div class="bg-[var(--el-bg-color)]">
|
||||||
<!-- 进群审批:仅群主可操作;开启后普通成员的「申请」「邀请」路径都需群主 / 管理员同意;群主 / 管理员邀请直进 -->
|
<!-- 进群审批:仅群主可操作;开启后普通成员的「申请」「邀请」路径都需群主 / 管理员同意;群主 / 管理员邀请直进 -->
|
||||||
<div v-if="isOwner" class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
|
<div
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">进群需要群主 / 群管理确认</span>
|
v-if="isOwner"
|
||||||
|
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>进群需要群主 / 群管理确认</span
|
||||||
|
>
|
||||||
<el-switch :model-value="!!group.joinApproval" @change="handleJoinApprovalChange" />
|
<el-switch :model-value="!!group.joinApproval" @change="handleJoinApprovalChange" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 进群申请子项:仅当开启审批 + 当前用户是 owner / admin 时出现;点击进列表 dialog -->
|
<!-- 进群申请子项:仅当开启审批 + 当前用户是 owner / admin 时出现;点击进列表 dialog -->
|
||||||
|
|
@ -306,7 +363,9 @@
|
||||||
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
@click="handleOpenRequestList"
|
@click="handleOpenRequestList"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">- 进群申请</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>- 进群申请</span
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:right-outlined"
|
icon="ant-design:right-outlined"
|
||||||
:size="11"
|
:size="11"
|
||||||
|
|
@ -325,7 +384,9 @@
|
||||||
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
@click="handleOpenAdminSet"
|
@click="handleOpenAdminSet"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">群管理员</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>群管理员</span
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:right-outlined"
|
icon="ant-design:right-outlined"
|
||||||
:size="11"
|
:size="11"
|
||||||
|
|
@ -336,7 +397,9 @@
|
||||||
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
@click="handleOpenTransferOwner"
|
@click="handleOpenTransferOwner"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">群主管理权转让</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>群主管理权转让</span
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:right-outlined"
|
icon="ant-design:right-outlined"
|
||||||
:size="11"
|
:size="11"
|
||||||
|
|
@ -363,13 +426,7 @@
|
||||||
解散群聊
|
解散群聊
|
||||||
</el-button>
|
</el-button>
|
||||||
<!-- 非群主:退出群聊 -->
|
<!-- 非群主:退出群聊 -->
|
||||||
<el-button
|
<el-button v-else class="w-full !h-9 text-14px" type="danger" plain @click="handleQuit">
|
||||||
v-else
|
|
||||||
class="w-full !h-9 text-14px"
|
|
||||||
type="danger"
|
|
||||||
plain
|
|
||||||
@click="handleQuit"
|
|
||||||
>
|
|
||||||
退出群聊
|
退出群聊
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -399,11 +456,7 @@ import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
|
||||||
import { getCurrentUserId } from '@/utils/auth'
|
import { getCurrentUserId } from '@/utils/auth'
|
||||||
import { CommonStatusEnum } from '@/utils/constants'
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
import {
|
import { updateGroup, muteAll, dissolveGroup } from '@/api/im/group'
|
||||||
updateGroup,
|
|
||||||
muteAll,
|
|
||||||
dissolveGroup
|
|
||||||
} from '@/api/im/group'
|
|
||||||
import { quitGroup, updateGroupMember } from '@/api/im/group/member'
|
import { quitGroup, updateGroupMember } from '@/api/im/group/member'
|
||||||
import { useConversationStore } from '../../../../store/conversationStore'
|
import { useConversationStore } from '../../../../store/conversationStore'
|
||||||
import { useGroupStore } from '../../../../store/groupStore'
|
import { useGroupStore } from '../../../../store/groupStore'
|
||||||
|
|
@ -644,7 +697,11 @@ function onTopChange(value: boolean | string | number) {
|
||||||
if (!props.conversation) {
|
if (!props.conversation) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conversationStore.setConversationTop(props.conversation.type, props.conversation.targetId, !!value)
|
conversationStore.setConversationTop(
|
||||||
|
props.conversation.type,
|
||||||
|
props.conversation.targetId,
|
||||||
|
!!value
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 全群禁言 ====================
|
// ==================== 全群禁言 ====================
|
||||||
|
|
@ -813,13 +870,13 @@ function handleOpenTransferOwner() {
|
||||||
/* 「添加 / 移出」瓦片:hover 时联动内部 icon-tile 走主色,跨子元素的 hover 联动无法用单元素工具类表达 */
|
/* 「添加 / 移出」瓦片:hover 时联动内部 icon-tile 走主色,跨子元素的 hover 联动无法用单元素工具类表达 */
|
||||||
.im-conversation-group-side__tile-wrap:hover .im-conversation-group-side__icon-tile {
|
.im-conversation-group-side__tile-wrap:hover .im-conversation-group-side__icon-tile {
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
border-color: var(--el-color-primary);
|
|
||||||
background-color: var(--el-color-primary-light-9);
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* :deep 穿透 Icon 内部 svg; el-icon 全局 color 在暗色模式下被主题盖过,锁 fill 到当前色 */
|
/* :deep 穿透 Icon 内部 svg; el-icon 全局 color 在暗色模式下被主题盖过,锁 fill 到当前色 */
|
||||||
.im-conversation-group-side__icon-tile :deep(svg) {
|
.im-conversation-group-side__icon-tile :deep(svg) {
|
||||||
fill: currentColor !important;
|
fill: currentcolor !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 相邻信息行加分隔线; 相邻兄弟选择器无法用工具类表达 */
|
/* 相邻信息行加分隔线; 相邻兄弟选择器无法用工具类表达 */
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,8 @@ const requestText = computed(() => {
|
||||||
if (!isGroup.value) {
|
if (!isGroup.value) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
const count = groupRequestStore.getUnhandledGroupRequestCountMap.get(props.conversation.targetId) ?? 0
|
const count =
|
||||||
|
groupRequestStore.getUnhandledGroupRequestCountMap.get(props.conversation.targetId) ?? 0
|
||||||
return count > 0 ? `[${count}条进群申请]` : ''
|
return count > 0 ? `[${count}条进群申请]` : ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -293,7 +294,6 @@ function handleContextMenu(e: MouseEvent) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
@ -304,7 +304,7 @@ function handleContextMenu(e: MouseEvent) {
|
||||||
|
|
||||||
/* el-icon 的全局 color:var(--color) 在暗色模式下会渲染成白色,这里用 :deep + !important 锁定 */
|
/* el-icon 的全局 color:var(--color) 在暗色模式下会渲染成白色,这里用 :deep + !important 锁定 */
|
||||||
.conversation-item__silent :deep(svg) {
|
.conversation-item__silent :deep(svg) {
|
||||||
fill: currentColor !important;
|
fill: currentcolor !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.conversation-item__prefix {
|
.conversation-item__prefix {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@
|
||||||
:name="friend.nickname"
|
:name="friend.nickname"
|
||||||
:size="50"
|
:size="50"
|
||||||
/>
|
/>
|
||||||
<div class="w-full mt-1.5 overflow-hidden text-12px leading-[1.5] text-[var(--el-text-color-regular)] text-center truncate">
|
<div
|
||||||
|
class="w-full mt-1.5 overflow-hidden text-12px leading-[1.5] text-[var(--el-text-color-regular)] text-center truncate"
|
||||||
|
>
|
||||||
{{ displayName }}
|
{{ displayName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -43,10 +45,15 @@
|
||||||
title="发起群聊"
|
title="发起群聊"
|
||||||
@click="handleOpenCreateGroup"
|
@click="handleOpenCreateGroup"
|
||||||
>
|
>
|
||||||
<div class="im-conversation-private-side__icon-tile flex items-center justify-center w-[50px] h-[50px] text-20px text-[var(--el-text-color-regular)] bg-[var(--el-fill-color-lighter)] border border-dashed border-[var(--el-border-color)] rounded-md transition-colors duration-200">
|
<div
|
||||||
|
class="im-conversation-private-side__icon-tile flex items-center justify-center w-[50px] h-[50px] text-20px text-[var(--el-text-color-regular)] bg-[var(--el-fill-color-lighter)] border border-dashed border-[var(--el-border-color)] rounded-md transition-colors duration-200"
|
||||||
|
>
|
||||||
<Icon icon="ant-design:plus-outlined" />
|
<Icon icon="ant-design:plus-outlined" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full mt-1.5 overflow-hidden text-12px leading-[1.5] text-[var(--el-text-color-regular)] text-center truncate">添加</div>
|
<div
|
||||||
|
class="w-full mt-1.5 overflow-hidden text-12px leading-[1.5] text-[var(--el-text-color-regular)] text-center truncate"
|
||||||
|
>添加</div
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -64,14 +71,19 @@
|
||||||
<div
|
<div
|
||||||
class="im-conversation-private-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-private-side__row flex flex-col items-stretch gap-1.5 px-4 py-[14px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">备注</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>备注</span
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
v-if="friend.displayName"
|
v-if="friend.displayName"
|
||||||
class="text-13px leading-[1.6] text-[var(--el-text-color-regular)] break-all line-clamp-2"
|
class="text-13px leading-[1.6] text-[var(--el-text-color-regular)] break-all line-clamp-2"
|
||||||
>
|
>
|
||||||
{{ friend.displayName }}
|
{{ friend.displayName }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-13px leading-[1.6] text-[var(--el-text-color-placeholder)]">
|
<span
|
||||||
|
v-else
|
||||||
|
class="text-13px leading-[1.6] text-[var(--el-text-color-placeholder)]"
|
||||||
|
>
|
||||||
好友备注仅自己可见
|
好友备注仅自己可见
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -101,7 +113,9 @@
|
||||||
class="im-conversation-private-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
class="im-conversation-private-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 cursor-pointer transition-colors duration-150 hover:bg-[var(--el-fill-color-lighter)]"
|
||||||
@click="emit('open-history')"
|
@click="emit('open-history')"
|
||||||
>
|
>
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">查找聊天内容</span>
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>查找聊天内容</span
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:right-outlined"
|
icon="ant-design:right-outlined"
|
||||||
:size="11"
|
:size="11"
|
||||||
|
|
@ -114,12 +128,20 @@
|
||||||
|
|
||||||
<!-- 开关项 -->
|
<!-- 开关项 -->
|
||||||
<div class="bg-[var(--el-bg-color)]">
|
<div class="bg-[var(--el-bg-color)]">
|
||||||
<div class="im-conversation-private-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
|
<div
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">消息免打扰</span>
|
class="im-conversation-private-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>消息免打扰</span
|
||||||
|
>
|
||||||
<el-switch :model-value="!!conversation?.silent" @change="handleMutedChange" />
|
<el-switch :model-value="!!conversation?.silent" @change="handleMutedChange" />
|
||||||
</div>
|
</div>
|
||||||
<div class="im-conversation-private-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
|
<div
|
||||||
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]">置顶聊天</span>
|
class="im-conversation-private-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
<span class="flex-shrink-0 text-14px text-[var(--el-text-color-primary)]"
|
||||||
|
>置顶聊天</span
|
||||||
|
>
|
||||||
<el-switch :model-value="!!conversation?.top" @change="handleTopChange" />
|
<el-switch :model-value="!!conversation?.top" @change="handleTopChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -236,7 +258,11 @@ function handleTopChange(value: boolean | string | number) {
|
||||||
if (!props.conversation) {
|
if (!props.conversation) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conversationStore.setConversationTop(props.conversation.type, props.conversation.targetId, !!value)
|
conversationStore.setConversationTop(
|
||||||
|
props.conversation.type,
|
||||||
|
props.conversation.targetId,
|
||||||
|
!!value
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 群创建成功:跳到新群会话 + 关掉本侧抽屉,让用户专注新群 */
|
/** 群创建成功:跳到新群会话 + 关掉本侧抽屉,让用户专注新群 */
|
||||||
|
|
@ -260,13 +286,13 @@ function handleGroupCreated(groupId: number) {
|
||||||
/* 「+」 tile: hover 时联动内部 icon-tile 走主色; 跨子元素的 hover 联动无法用单元素工具类表达 */
|
/* 「+」 tile: hover 时联动内部 icon-tile 走主色; 跨子元素的 hover 联动无法用单元素工具类表达 */
|
||||||
.im-conversation-private-side__tile-wrap-clickable:hover .im-conversation-private-side__icon-tile {
|
.im-conversation-private-side__tile-wrap-clickable:hover .im-conversation-private-side__icon-tile {
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
border-color: var(--el-color-primary);
|
|
||||||
background-color: var(--el-color-primary-light-9);
|
background-color: var(--el-color-primary-light-9);
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* :deep 穿透 Icon 内部 svg; el-icon 全局 color 在暗色模式下被主题盖过,锁 fill 到当前色 */
|
/* :deep 穿透 Icon 内部 svg; el-icon 全局 color 在暗色模式下被主题盖过,锁 fill 到当前色 */
|
||||||
.im-conversation-private-side__icon-tile :deep(svg) {
|
.im-conversation-private-side__icon-tile :deep(svg) {
|
||||||
fill: currentColor !important;
|
fill: currentcolor !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 相邻信息行加分隔线; 相邻兄弟选择器无法用工具类表达 */
|
/* 相邻信息行加分隔线; 相邻兄弟选择器无法用工具类表达 */
|
||||||
|
|
|
||||||
|
|
@ -342,14 +342,13 @@ onUnmounted(() => {
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 底部小三角:指向触发图标,仿微信 PC 气泡指针;left 偏移对应表情按钮(工具栏 1st icon) */
|
/* 底部小三角:指向触发图标,仿微信 PC 气泡指针;left 偏移对应表情按钮(工具栏 1st icon) */
|
||||||
.im-popover-arrow::after {
|
.im-popover-arrow::after {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(100% - 1px);
|
top: calc(100% - 1px);
|
||||||
left: 10px;
|
left: 10px;
|
||||||
border-style: solid;
|
|
||||||
border-width: 6px 6px 0 6px;
|
|
||||||
border-color: var(--el-bg-color) transparent transparent transparent;
|
border-color: var(--el-bg-color) transparent transparent transparent;
|
||||||
filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.08));
|
border-style: solid;
|
||||||
|
border-width: 6px 6px 0;
|
||||||
|
content: '';
|
||||||
|
filter: drop-shadow(0 2px 2px rgb(0 0 0 / 8%));
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,7 @@ function collectFromEditor(root: HTMLElement): { text: string; atUserIds: number
|
||||||
* 3. 二次防御:collectFromEditor 走 trim,可能比 syncEditorState 更严格(例如全 ZWSP),仍空就 return
|
* 3. 二次防御:collectFromEditor 走 trim,可能比 syncEditorState 更严格(例如全 ZWSP),仍空就 return
|
||||||
* 4. 清空 + 同步状态:先清 innerHTML 再 syncEditorState 让 placeholder / canSend 一起回归
|
* 4. 清空 + 同步状态:先清 innerHTML 再 syncEditorState 让 placeholder / canSend 一起回归
|
||||||
* (顺序很重要:先清后 sync,否则 sync 看到旧内容会误判)
|
* (顺序很重要:先清后 sync,否则 sync 看到旧内容会误判)
|
||||||
* 5. 上送:atUserIds 非空才传,避免发空数组;quote 由 clearConversationDraft 前抓取,确保引用条立即消失
|
* 5. 上送:atUserIds 非空才传,避免发空数组;quote 由 clearConversationDraft 前抓取,确保引用条立即消失
|
||||||
*/
|
*/
|
||||||
async function handleSend(options?: { receipt?: boolean }) {
|
async function handleSend(options?: { receipt?: boolean }) {
|
||||||
const editor = editorRef.value
|
const editor = editorRef.value
|
||||||
|
|
@ -1171,8 +1171,9 @@ async function onVideoPicked(e: Event) {
|
||||||
.message-input__tool:deep(svg) {
|
.message-input__tool:deep(svg) {
|
||||||
font-size: 18px !important;
|
font-size: 18px !important;
|
||||||
color: var(--el-text-color-regular) !important;
|
color: var(--el-text-color-regular) !important;
|
||||||
fill: currentColor !important;
|
fill: currentcolor !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-input__tool:hover,
|
.message-input__tool:hover,
|
||||||
.message-input__tool:hover:deep(svg) {
|
.message-input__tool:hover:deep(svg) {
|
||||||
color: var(--el-color-primary) !important;
|
color: var(--el-color-primary) !important;
|
||||||
|
|
@ -1180,10 +1181,10 @@ async function onVideoPicked(e: Event) {
|
||||||
|
|
||||||
/* 用 data-empty 而非 :empty:浏览器在删空后会留下 <br>,:empty 不命中;data-empty 由 syncEditorState 维护 */
|
/* 用 data-empty 而非 :empty:浏览器在删空后会留下 <br>,:empty 不命中;data-empty 由 syncEditorState 维护 */
|
||||||
.message-input__editor[data-empty]::before {
|
.message-input__editor[data-empty]::before {
|
||||||
content: attr(data-placeholder);
|
position: absolute;
|
||||||
color: var(--el-text-color-placeholder);
|
color: var(--el-text-color-placeholder);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
content: attr(data-placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @ token 走主色高亮;contenteditable=false 让 backspace 整段删而不是逐字符 */
|
/* @ token 走主色高亮;contenteditable=false 让 backspace 整段删而不是逐字符 */
|
||||||
|
|
|
||||||
|
|
@ -54,15 +54,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { computed, onBeforeUnmount, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue'
|
||||||
computed,
|
|
||||||
onBeforeUnmount,
|
|
||||||
onMounted,
|
|
||||||
onUnmounted,
|
|
||||||
ref,
|
|
||||||
useTemplateRef,
|
|
||||||
watch
|
|
||||||
} from 'vue'
|
|
||||||
import { useMessage } from '@/hooks/web/useMessage'
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
import { formatSeconds } from '@/utils/formatTime'
|
import { formatSeconds } from '@/utils/formatTime'
|
||||||
|
|
||||||
|
|
@ -292,14 +284,14 @@ onUnmounted(() => {
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 底部小三角:指向触发图标,仿微信 PC 气泡指针;left 偏移对应语音按钮(工具栏 4th icon) */
|
/* 底部小三角:指向触发图标,仿微信 PC 气泡指针;left 偏移对应语音按钮(工具栏 4th icon) */
|
||||||
.im-popover-arrow::after {
|
.im-popover-arrow::after {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(100% - 1px);
|
top: calc(100% - 1px);
|
||||||
left: 110px;
|
left: 110px;
|
||||||
border-style: solid;
|
|
||||||
border-width: 6px 6px 0 6px;
|
|
||||||
border-color: var(--el-bg-color) transparent transparent transparent;
|
border-color: var(--el-bg-color) transparent transparent transparent;
|
||||||
filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.08));
|
border-style: solid;
|
||||||
|
border-width: 6px 6px 0;
|
||||||
|
content: '';
|
||||||
|
filter: drop-shadow(0 2px 2px rgb(0 0 0 / 8%));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 录音中的脉冲呼吸动画;@keyframes 必须 CSS 定义 */
|
/* 录音中的脉冲呼吸动画;@keyframes 必须 CSS 定义 */
|
||||||
|
|
@ -309,13 +301,15 @@ onUnmounted(() => {
|
||||||
|
|
||||||
@keyframes im-voice-pulse {
|
@keyframes im-voice-pulse {
|
||||||
0% {
|
0% {
|
||||||
box-shadow: 0 0 0 0 rgba(245, 108, 108, 0.6);
|
box-shadow: 0 0 0 0 rgb(245 108 108 / 60%);
|
||||||
}
|
}
|
||||||
|
|
||||||
70% {
|
70% {
|
||||||
box-shadow: 0 0 0 20px rgba(245, 108, 108, 0);
|
box-shadow: 0 0 0 20px rgb(245 108 108 / 0%);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
box-shadow: 0 0 0 0 rgba(245, 108, 108, 0);
|
box-shadow: 0 0 0 0 rgb(245 108 108 / 0%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -182,15 +182,15 @@ async function handleRemove(pinnedMessage: Message) {
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 弹出层朝上的三角箭头;走 ::before + 4 边 border 配色画,颜色跟弹出层 background 一致 */
|
/* 弹出层朝上的三角箭头;走 ::before + 4 边 border 配色画,颜色跟弹出层 background 一致 */
|
||||||
.im-group-pinned-message__list::before {
|
.im-group-pinned-message__list::before {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -8px;
|
top: -8px;
|
||||||
left: 184px;
|
left: 184px;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-left: 8px solid transparent;
|
|
||||||
border-right: 8px solid transparent;
|
border-right: 8px solid transparent;
|
||||||
border-bottom: 8px solid var(--el-bg-color);
|
border-bottom: 8px solid var(--el-bg-color);
|
||||||
filter: drop-shadow(0 -2px 1px rgba(0, 0, 0, 0.04));
|
border-left: 8px solid transparent;
|
||||||
|
content: '';
|
||||||
|
filter: drop-shadow(0 -2px 1px rgb(0 0 0 / 4%));
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,6 @@ const pendingCount = computed(() => groupRequestStore.getUnhandledGroupRequestCo
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* :deep 穿透 Icon 子组件 DOM;强制 svg 走 currentColor 应对暗色模式 el-icon 全局色覆盖 */
|
/* :deep 穿透 Icon 子组件 DOM;强制 svg 走 currentColor 应对暗色模式 el-icon 全局色覆盖 */
|
||||||
.im-group-request-pending__icon :deep(svg) {
|
.im-group-request-pending__icon :deep(svg) {
|
||||||
fill: currentColor !important;
|
fill: currentcolor !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,14 @@
|
||||||
class="material-card cursor-pointer w-full overflow-hidden rounded-lg bg-[var(--el-bg-color)] border border-solid border-[var(--el-border-color-lighter)]"
|
class="material-card cursor-pointer w-full overflow-hidden rounded-lg bg-[var(--el-bg-color)] border border-solid border-[var(--el-border-color-lighter)]"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
<img v-if="payload.coverUrl" class="block w-full h-[200px] object-cover" :src="payload.coverUrl" />
|
<img
|
||||||
<div class="px-3.5 py-3 text-15px font-600 leading-[1.4] text-[var(--el-text-color-primary)] line-clamp-2">
|
v-if="payload.coverUrl"
|
||||||
|
class="block w-full h-[200px] object-cover"
|
||||||
|
:src="payload.coverUrl"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="px-3.5 py-3 text-15px font-600 leading-[1.4] text-[var(--el-text-color-primary)] line-clamp-2"
|
||||||
|
>
|
||||||
{{ payload.title || '(无标题)' }}
|
{{ payload.title || '(无标题)' }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -19,7 +25,9 @@
|
||||||
>
|
>
|
||||||
<div class="flex gap-2.5 items-start">
|
<div class="flex gap-2.5 items-start">
|
||||||
<div class="flex flex-1 flex-col gap-1.5 min-w-0">
|
<div class="flex flex-1 flex-col gap-1.5 min-w-0">
|
||||||
<div class="text-15px font-600 leading-[1.4] text-[var(--el-text-color-primary)] line-clamp-2 break-all">
|
<div
|
||||||
|
class="text-15px font-600 leading-[1.4] text-[var(--el-text-color-primary)] line-clamp-2 break-all"
|
||||||
|
>
|
||||||
{{ payload.title || '(无标题)' }}
|
{{ payload.title || '(无标题)' }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -35,7 +43,9 @@
|
||||||
:src="payload.coverUrl"
|
:src="payload.coverUrl"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1.5 mt-2.5 pt-2 border-t border-t-solid border-[var(--el-border-color-lighter)] text-12px text-[var(--el-text-color-secondary)]">
|
<div
|
||||||
|
class="flex items-center gap-1.5 mt-2.5 pt-2 border-t border-t-solid border-[var(--el-border-color-lighter)] text-12px text-[var(--el-text-color-secondary)]"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
v-if="sourceChannel?.avatar"
|
v-if="sourceChannel?.avatar"
|
||||||
class="w-4 h-4 rounded-full object-cover flex-shrink-0"
|
class="w-4 h-4 rounded-full object-cover flex-shrink-0"
|
||||||
|
|
@ -47,13 +57,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 富文本详情:全屏弹窗按需挂载,destroy-on-close 关闭后释放 v-dompurify-html 解析的 DOM -->
|
<!-- 富文本详情:全屏弹窗按需挂载,destroy-on-close 关闭后释放 v-dompurify-html 解析的 DOM -->
|
||||||
<Dialog
|
<Dialog v-model="detailVisible" :title="payload.title || '详情'" fullscreen destroy-on-close>
|
||||||
v-model="detailVisible"
|
<div
|
||||||
:title="payload.title || '详情'"
|
v-loading="detailLoading"
|
||||||
fullscreen
|
class="material-detail-body max-w-[720px] mx-auto px-5 pt-6 pb-20"
|
||||||
destroy-on-close
|
>
|
||||||
>
|
|
||||||
<div v-loading="detailLoading" class="material-detail-body max-w-[720px] mx-auto px-5 pt-6 pb-20">
|
|
||||||
<div class="text-[22px] font-600 leading-[1.4] text-[var(--el-text-color-primary)] mb-5">
|
<div class="text-[22px] font-600 leading-[1.4] text-[var(--el-text-color-primary)] mb-5">
|
||||||
{{ payload.title || '' }}
|
{{ payload.title || '' }}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -95,9 +103,7 @@ const isChannelView = computed(
|
||||||
)
|
)
|
||||||
|
|
||||||
/** 反序列化 content JSON 为 payload 对象 */
|
/** 反序列化 content JSON 为 payload 对象 */
|
||||||
const payload = computed<MaterialMessage>(
|
const payload = computed<MaterialMessage>(() => parseMessage<MaterialMessage>(props.content) ?? {})
|
||||||
() => parseMessage<MaterialMessage>(props.content) ?? {}
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 来源频道;紧凑卡底部渲染头像 + 名称 */
|
/** 来源频道;紧凑卡底部渲染头像 + 名称 */
|
||||||
const sourceChannel = computed(() =>
|
const sourceChannel = computed(() =>
|
||||||
|
|
@ -137,15 +143,15 @@ const onClick = async () => {
|
||||||
transition: box-shadow 0.15s ease;
|
transition: box-shadow 0.15s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 12px rgb(0 0 0 / 8%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* :deep 穿透 v-dompurify-html 渲染的内嵌 DOM;统一控制富文本里的 img / p / hN 排版 */
|
/* :deep 穿透 v-dompurify-html 渲染的内嵌 DOM;统一控制富文本里的 img / p / hN 排版 */
|
||||||
.article-content {
|
.article-content {
|
||||||
:deep(img) {
|
:deep(img) {
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
height: auto;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(p) {
|
:deep(p) {
|
||||||
|
|
|
||||||
|
|
@ -355,28 +355,31 @@ onBeforeUnmount(() => {
|
||||||
颜色与气泡背景对应,留 1px 视觉吃进去,省一张图片 */
|
颜色与气泡背景对应,留 1px 视觉吃进去,省一张图片 */
|
||||||
.message-bubble--other::before,
|
.message-bubble--other::before,
|
||||||
.message-bubble--self::before {
|
.message-bubble--self::before {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-bubble--other::before {
|
.message-bubble--other::before {
|
||||||
left: -5px;
|
left: -5px;
|
||||||
border-width: 5px 6px 5px 0;
|
|
||||||
border-color: transparent var(--el-fill-color-light) transparent transparent;
|
border-color: transparent var(--el-fill-color-light) transparent transparent;
|
||||||
|
border-width: 5px 6px 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-bubble--self::before {
|
.message-bubble--self::before {
|
||||||
right: -5px;
|
right: -5px;
|
||||||
border-width: 5px 0 5px 6px;
|
|
||||||
border-color: transparent transparent transparent #95ec69;
|
border-color: transparent transparent transparent #95ec69;
|
||||||
|
border-width: 5px 0 5px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* :deep 穿透 scoped 子组件 DOM;el-icon 在暗色模式下全局 color 被 .el-icon{color:var(--color)} 干扰,把 voice 图标 fill 锁死 */
|
/* :deep 穿透 scoped 子组件 DOM;el-icon 在暗色模式下全局 color 被 .el-icon{color:var(--color)} 干扰,把 voice 图标 fill 锁死 */
|
||||||
.message-bubble__voice-icon :deep(svg) {
|
.message-bubble__voice-icon :deep(svg) {
|
||||||
fill: #606266 !important;
|
fill: #606266 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-bubble__voice-icon.im-voice-playing :deep(svg) {
|
.message-bubble__voice-icon.im-voice-playing :deep(svg) {
|
||||||
fill: #409eff !important;
|
fill: #409eff !important;
|
||||||
}
|
}
|
||||||
|
|
@ -391,6 +394,7 @@ onBeforeUnmount(() => {
|
||||||
100% {
|
100% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
transform: scale(1.15);
|
transform: scale(1.15);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -714,8 +714,8 @@ function locateMessage(messageId: number) {
|
||||||
/* :deep 穿透 el-tag 子组件 DOM;搜索区 chip 禁掉 hover 颜色过渡 / × 图标动效,避免在搜索区里有抖动感 */
|
/* :deep 穿透 el-tag 子组件 DOM;搜索区 chip 禁掉 hover 颜色过渡 / × 图标动效,避免在搜索区里有抖动感 */
|
||||||
.im-message-history__chip,
|
.im-message-history__chip,
|
||||||
.im-message-history__chip :deep(.el-tag__close) {
|
.im-message-history__chip :deep(.el-tag__close) {
|
||||||
transition: none !important;
|
|
||||||
animation: none !important;
|
animation: none !important;
|
||||||
|
transition: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 「定位到聊天位置」按钮:父行 hover 才显示,走 .parent:hover .child 跨元素状态联动 */
|
/* 「定位到聊天位置」按钮:父行 hover 才显示,走 .parent:hover .child 跨元素状态联动 */
|
||||||
|
|
@ -723,16 +723,18 @@ function locateMessage(messageId: number) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
right: 0;
|
right: 0;
|
||||||
margin-top: 4px;
|
|
||||||
display: none;
|
display: none;
|
||||||
white-space: nowrap;
|
margin-top: 4px;
|
||||||
color: #1989fa;
|
color: #1989fa;
|
||||||
|
white-space: nowrap;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 0.15s;
|
transition: color 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-message-history__row:hover .im-message-history__locate {
|
.im-message-history__row:hover .im-message-history__locate {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-message-history__locate:hover {
|
.im-message-history__locate:hover {
|
||||||
color: #146fc7;
|
color: #146fc7;
|
||||||
}
|
}
|
||||||
|
|
@ -744,30 +746,35 @@ function locateMessage(messageId: number) {
|
||||||
color: #1989fa;
|
color: #1989fa;
|
||||||
transition: color 0.15s;
|
transition: color 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-message-history__tab:hover {
|
.im-message-history__tab:hover {
|
||||||
color: #2f81d4;
|
color: #2f81d4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-message-history__tab--active::after {
|
.im-message-history__tab--active::after {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: #1989fa;
|
background: #1989fa;
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* :deep 穿透 el-calendar 子组件 DOM;默认偏大压一压让它能塞进 320 popover */
|
/* :deep 穿透 el-calendar 子组件 DOM;默认偏大压一压让它能塞进 320 popover */
|
||||||
.im-message-history__calendar :deep(.el-calendar) {
|
.im-message-history__calendar :deep(.el-calendar) {
|
||||||
--el-calendar-cell-width: 36px;
|
--el-calendar-cell-width: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-message-history__calendar :deep(.el-calendar__header) {
|
.im-message-history__calendar :deep(.el-calendar__header) {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-message-history__calendar :deep(.el-calendar-table) {
|
.im-message-history__calendar :deep(.el-calendar-table) {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.im-message-history__calendar :deep(.el-calendar-day) {
|
.im-message-history__calendar :deep(.el-calendar-day) {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
|
|
||||||
|
|
@ -1053,6 +1053,7 @@ function handleDelete() {
|
||||||
.im-loading-spin {
|
.im-loading-spin {
|
||||||
animation: im-loading-spin 1s linear infinite;
|
animation: im-loading-spin 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes im-loading-spin {
|
@keyframes im-loading-spin {
|
||||||
to {
|
to {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
|
|
|
||||||
|
|
@ -755,9 +755,10 @@ watch(
|
||||||
.message-panel__header-icon,
|
.message-panel__header-icon,
|
||||||
.message-panel__header-icon :deep(svg) {
|
.message-panel__header-icon :deep(svg) {
|
||||||
color: var(--el-text-color-regular) !important;
|
color: var(--el-text-color-regular) !important;
|
||||||
fill: currentColor !important;
|
fill: currentcolor !important;
|
||||||
transition: color 0.15s;
|
transition: color 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-panel__header-icon:hover,
|
.message-panel__header-icon:hover,
|
||||||
.message-panel__header-icon:hover :deep(svg) {
|
.message-panel__header-icon:hover :deep(svg) {
|
||||||
color: var(--el-color-primary) !important;
|
color: var(--el-color-primary) !important;
|
||||||
|
|
@ -775,6 +776,7 @@ watch(
|
||||||
.message-panel__message-anchor {
|
.message-panel__message-anchor {
|
||||||
transition: background-color 0.6s ease;
|
transition: background-color 0.6s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-panel__message-anchor--highlight {
|
.message-panel__message-anchor--highlight {
|
||||||
background-color: var(--el-color-warning-light-9);
|
background-color: var(--el-color-warning-light-9);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue