更新了多个文件以优化样式和功能,包括在UnoCSS配置中添加预设,调整了主题颜色和消息组件的样式,更新了环境变量,修复了头像组件的尺寸,并在消息面板中添加了多选功能。

This commit is contained in:
Phoenix 2025-05-12 13:44:13 +08:00
parent d2c8de16bb
commit d413a6b9fe
26 changed files with 112 additions and 73 deletions

View File

@ -5,4 +5,4 @@
"singleQuote": true, "singleQuote": true,
"printWidth": 100, "printWidth": 100,
"trailingComma": "none" "trailingComma": "none"
} }

4
env/.env.test vendored
View File

@ -2,7 +2,7 @@ ENV = 'development'
VITE_BASE=/ VITE_BASE=/
VUE_APP_PREVIEW=false VUE_APP_PREVIEW=false
VITE_BASE_API=http://172.16.100.93:8503 VITE_BASE_API=http://114.218.158.24:8503
VITE_EPR_BASEURL=http://114.218.158.24:9020 VITE_EPR_BASEURL=http://114.218.158.24:9020
VITE_SOCKET_API=ws://172.16.100.93:8504 VITE_SOCKET_API=ws://114.218.158.24:8504
VUE_APP_WEBSITE_NAME="Lumen IM" VUE_APP_WEBSITE_NAME="Lumen IM"

View File

@ -34,11 +34,11 @@
"xgplayer": "^3.0.4" "xgplayer": "^3.0.4"
}, },
"devDependencies": { "devDependencies": {
"vite-plugin-vue-devtools": "^7.7.6",
"@icon-park/vue-next": "^1.4.2", "@icon-park/vue-next": "^1.4.2",
"@tsconfig/node18": "^18.2.2", "@tsconfig/node18": "^18.2.2",
"@types/node": "^18.18.5", "@types/node": "^18.18.5",
"@types/vue": "^2.0.0", "@types/vue": "^2.0.0",
"@unocss/reset": "^66.1.1",
"@vitejs/plugin-vue": "^4.4.0", "@vitejs/plugin-vue": "^4.4.0",
"@vitejs/plugin-vue-jsx": "^3.0.2", "@vitejs/plugin-vue-jsx": "^3.0.2",
"@vue/tsconfig": "^0.4.0", "@vue/tsconfig": "^0.4.0",
@ -53,6 +53,7 @@
"unocss": "^66.1.1", "unocss": "^66.1.1",
"vite": "^4.5.1", "vite": "^4.5.1",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "^7.7.6",
"vue-tsc": "^1.8.25", "vue-tsc": "^1.8.25",
"wait-on": "^6.0.1" "wait-on": "^6.0.1"
}, },

View File

@ -75,6 +75,9 @@ importers:
'@types/vue': '@types/vue':
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0(typescript@5.2.2) version: 2.0.0(typescript@5.2.2)
'@unocss/reset':
specifier: ^66.1.1
version: 66.1.1
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: ^4.4.0 specifier: ^4.4.0
version: 4.6.2(vite@4.5.14(@types/node@18.19.99)(less@4.3.0)(terser@5.39.0))(vue@3.5.13(typescript@5.2.2)) version: 4.6.2(vite@4.5.14(@types/node@18.19.99)(less@4.3.0)(terser@5.39.0))(vue@3.5.13(typescript@5.2.2))

View File

@ -1,6 +1,7 @@
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box!important;
} }
@font-face { @font-face {

View File

@ -4,7 +4,7 @@ html {
--im-bg-color: #ffffff; --im-bg-color: #ffffff;
--line-border-color: #f5f5f5; --line-border-color: #f5f5f5;
--border-color: #eeeaea; --border-color: #eeeaea;
--im-text-color: #333; --im-text-color: #BABABA;
--im-text-color-grey: #333; --im-text-color-grey: #333;
--im-active-bg-color: #f5f5f5; --im-active-bg-color: #f5f5f5;
--im-hover-bg-color: #f5f5f5; --im-hover-bg-color: #f5f5f5;
@ -21,10 +21,10 @@ html {
// message // message
--im-message-bg-color: #f7f7f7; --im-message-bg-color: #f7f7f7;
--im-message-border-color: #efeff5; --im-message-border-color: #efeff5;
--im-message-left-bg-color: #eff0f1; --im-message-left-bg-color: #F4F4FC;
--im-message-left-text-color: #333; --im-message-left-text-color: #333;
--im-message-right-bg-color: #daf3fd; --im-message-right-bg-color: #46299D;
--im-message-right-text-color: #333; --im-message-right-text-color: #fff;
} }
// 黑色主题 // 黑色主题

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -87,8 +87,8 @@ const text_avatar = computed(() => {
background: linear-gradient(to right, #674bbc, #46299d); background: linear-gradient(to right, #674bbc, #46299d);
flex-shrink: 0; flex-shrink: 0;
img { img {
width: 100%; width: 42px;
height: 100%; height: 42px;
object-fit: cover; object-fit: cover;
} }
} }

View File

@ -46,7 +46,8 @@ const img = (src: string, width = 200) => {
background: var(--im-message-left-bg-color); background: var(--im-message-left-bg-color);
min-width: 30px; min-width: 30px;
min-height: 30px; min-height: 30px;
max-width:240px;
max-height:300px
&.left { &.left {
background: var(--im-message-right-bg-color); background: var(--im-message-right-bg-color);
} }

View File

@ -45,7 +45,7 @@ textContent = textReplaceEmoji(textContent)
color: var(--im-message-left-text-color); color: var(--im-message-left-text-color);
background: var(--im-message-left-bg-color); background: var(--im-message-left-bg-color);
border-radius: 0px 10px 10px 10px; border-radius: 0px 10px 10px 10px;
font-size: 14px;
&.right { &.right {
background-color: var(--im-message-right-bg-color); background-color: var(--im-message-right-bg-color);
color: var(--im-message-right-text-color); color: var(--im-message-right-text-color);

View File

@ -1,14 +1,28 @@
// 主题配置 // 主题配置
const primaryColor='#46299D'
export const overrides = { export const overrides = {
common: { DataTable: {
primaryColor: '#1890ff', sorterIconColor:'#fff',
primaryColorHover: '#1890ff', thColorHover: primaryColor,
primaryColorPressed: '#1890ff', thTextColor: "#fff",
primaryColorSuppl: '#1890ff', thColor: primaryColor,
bodyColor: '#ffffff' thBackgroundColor: primaryColor,
itemColorActive:primaryColor,
}, },
Button: {
Dialog: { textColor: primaryColor,
borderRadius: '10px' },
Dropdown:{
optionTextColorHover:'#46299D',
optionColorHover:'#EEE9F8'
},
common: {
primaryColorPressed: primaryColor,
primaryHover:primaryColor,
primaryDefault: primaryColor,
primaryActive: primaryColor,
primarySuppl: primaryColor,
primaryColor: primaryColor,
primaryColorHover: primaryColor
} }
} }

View File

@ -7,21 +7,21 @@ const settingsStore = useSettingsStore()
</script> </script>
<template> <template>
<section class="container flex-center"> <section class="top-container flex-center">
<section <section
class="el-container im-container" class="el-container im-container"
:class="{ :class="{
'small-screen': !settingsStore.isFullScreen 'small-screen': !settingsStore.isFullScreen
}" }"
> >
<aside <!-- <aside
class="el-aside" class="el-aside"
:class="{ :class="{
'pd-t15': isElectronMode() 'pd-t15': isElectronMode()
}" }"
> >
<Menu /> <Menu />
</aside> </aside> -->
<main class="el-main"> <main class="el-main">
<router-view /> <router-view />
</main> </main>
@ -30,8 +30,8 @@ const settingsStore = useSettingsStore()
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>
.container { .top-container {
width: 100%;
background: url(@/assets/image/background.jpeg); background: url(@/assets/image/background.jpeg);
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
@ -39,8 +39,8 @@ const settingsStore = useSettingsStore()
background-size: cover; background-size: cover;
.im-container { .im-container {
height: 80vh; height: 85vh;
width: 100vw; width: 100%;
overflow: hidden; overflow: hidden;
background-color: #fff; background-color: #fff;

View File

@ -5,7 +5,7 @@ import { createApp } from 'vue'
import router from './router' import router from './router'
import App from './App.vue' import App from './App.vue'
import * as plugins from './plugins' import * as plugins from './plugins'
import 'uno.css'
async function bootstrap() { async function bootstrap() {
const app = createApp(App) const app = createApp(App)

View File

@ -125,7 +125,7 @@ onMounted(() => {
<n-input <n-input
placeholder="搜索好友 / 群聊" placeholder="搜索好友 / 群聊"
v-model:value.trim="searchKeyword" v-model:value.trim="searchKeyword"
round
clearable clearable
style="width: 78%" style="width: 78%"
> >
@ -142,7 +142,7 @@ onMounted(() => {
</header> </header>
<!-- 置顶栏目 --> <!-- 置顶栏目 -->
<header class="el-header header-top" v-show="loadStatus == 3 && topItems.length > 0"> <!-- <header class="el-header header-top" v-show="loadStatus == 3 && topItems.length > 0">
<n-popover v-for="item in topItems" :key="item.index_name" placement="bottom" trigger="hover"> <n-popover v-for="item in topItems" :key="item.index_name" placement="bottom" trigger="hover">
<template #trigger> <template #trigger>
<div <div
@ -165,10 +165,10 @@ onMounted(() => {
</template> </template>
<span> {{ item.remark || item.name }} </span> <span> {{ item.remark || item.name }} </span>
</n-popover> </n-popover>
</header> </header> -->
<!-- 标题栏目 --> <!-- 标题栏目 -->
<header <!-- <header
v-show="loadStatus == 3 && talkStore.talkItems.length > 0" v-show="loadStatus == 3 && talkStore.talkItems.length > 0"
class="el-header header-badge" class="el-header header-badge"
:class="{ shadow: false }" :class="{ shadow: false }"
@ -177,7 +177,7 @@ onMounted(() => {
<p> <p>
<span class="badge unread" v-show="unreadNum">{{ unreadNum }}未读</span> <span class="badge unread" v-show="unreadNum">{{ unreadNum }}未读</span>
</p> </p>
</header> </header> -->
<main id="talk-session-list" class="el-main me-scrollbar me-scrollbar-thumb"> <main id="talk-session-list" class="el-main me-scrollbar me-scrollbar-thumb">
<template v-if="loadStatus == 2"><Skeleton /></template> <template v-if="loadStatus == 2"><Skeleton /></template>

View File

@ -10,16 +10,23 @@ defineProps({
username: String, username: String,
active: Boolean active: Boolean
}) })
//1=2=3=4=/
const labelColor=[
{group_type:2,color:'#377EC6',label:'部门'},
{group_type:3,color:'#C1691C',label:'项目'},
{group_type:4,color:'#7A58DE',label:'公司'},
]
</script> </script>
<template> <template>
<div class="talk pointer" :class="{ actived: active }" @click="emit('tab-talk', data)"> <div class="talk pointer" :class="{ actived: active }" @click="emit('tab-talk', data)">
<div class="avatar-box"> <div class="avatar-box relative">
<avatarModule :mode="data?.group_type === 0 ? 1 : 2" <avatarModule :mode="data?.group_type === 0 ? 1 : 2"
:avatar="data?.avatar" :avatar="data?.avatar"
:groupType="data?.group_type" :groupType="data?.group_type"
:userName="data?.name" :customStyle="{width:'42px',height:'42px'}"></avatarModule> :userName="data?.name" :customStyle="{width:'42px',height:'42px'}"></avatarModule>
<div v-if="[2,3,4].includes(data.group_type)" class="absolute w-32px h-18px border-2px border-solid rounded-3px top-28px bg-#fff text-10px flex justify-center items-center leading-none" :style="`color:${labelColor.find(x=>x.group_type===data.group_type)?.color};border-color:${labelColor.find(x=>x.group_type===data.group_type)?.color}`">{{ labelColor.find(x=>x.group_type===data.group_type)?.label }}</div>
<!-- <im-avatar :src="avatar" :size="34" :username="data.name" /> --> <!-- <im-avatar :src="avatar" :size="34" :username="data.name" /> -->
<div class="top-mask" @click.stop="emit('top-talk', data)"> <div class="top-mask" @click.stop="emit('top-talk', data)">
<n-icon :component="data.is_top == 1 ? ArrowDown : ArrowUp" /> <n-icon :component="data.is_top == 1 ? ArrowDown : ArrowUp" />
@ -30,9 +37,9 @@ defineProps({
<div class="header"> <div class="header">
<div class="title"> <div class="title">
<span class="nickname">{{ username }}</span> <span class="nickname">{{ username }}</span>
<span class="badge top" v-show="data.is_top"></span> <!-- <span class="badge top" v-show="data.is_top"></span>
<span class="badge roboot" v-show="data.is_robot"></span> <span class="badge roboot" v-show="data.is_robot"></span>
<span class="badge group" v-show="data.talk_type == 2"></span> <span class="badge group" v-show="data.talk_type == 2"></span> -->
</div> </div>
<div class="datetime"><Xtime :time="data.updated_at" /></div> <div class="datetime"><Xtime :time="data.updated_at" /></div>
</div> </div>
@ -80,8 +87,8 @@ defineProps({
border-radius: 5px; border-radius: 5px;
.avatar-box { .avatar-box {
height: 34px; height: 42px;
width: 34px; width: 42px;
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -90,7 +97,7 @@ defineProps({
user-select: none; user-select: none;
transition: ease 1s; transition: ease 1s;
position: relative; position: relative;
overflow: hidden;
.top-mask { .top-mask {
width: 100%; width: 100%;
@ -193,9 +200,9 @@ defineProps({
user-select: none; user-select: none;
.badge { .badge {
background-color: #f44336; background-color: #D03050;
color: #ffffff; color: #ffffff;
border-radius: 3px; border-radius: 50%;
transform-origin: right; transform-origin: right;
} }
} }

View File

@ -69,32 +69,42 @@ const onContactModal = (data: { id: number; type: number }[]) => {
<div class="multi-title"> <div class="multi-title">
<span>已选中{{ dialogueStore.selectItems.length }} 条消息</span> <span>已选中{{ dialogueStore.selectItems.length }} 条消息</span>
</div> </div>
<div class="multi-groups"> <div class="flex items-center relative">
<div class="btn-group"> <div class="multi-groups">
<div class="btn-group mr-156px">
<div class="multi-icon pointer flex-center" @click="onMergeForward"> <div class="multi-icon pointer flex-center" @click="onMergeForward">
<n-icon :size="22" :component="Share" /> <!-- <n-icon :size="22" :component="Share" /> -->
<img src="@/assets/image/zu6299@2x.png" class="w-72px h-72px" alt="">
</div> </div>
<p>合并转发</p> <p>合并转发</p>
</div> </div>
<div class="btn-group"> <div class="btn-group mr-156px">
<div class="multi-icon pointer flex-center" @click="onSingleForward"> <div class="multi-icon pointer flex-center" @click="onSingleForward">
<n-icon :size="22" :component="ShareThree" /> <!-- <n-icon :size="22" :component="ShareThree" /> -->
<img class="w-72px h-72px" src="@/assets/image/zu6300@2x.png">
</div> </div>
<p>逐条转发</p> <p>逐条转发</p>
</div> </div>
<div class="btn-group"> <div class="btn-group ">
<div class="multi-icon pointer flex-center" @click="onMultiDelete"> <div class="multi-icon pointer flex-center" @click="onMultiDelete">
<n-icon :size="22" :component="Delete" /> <!-- <n-icon :size="22" :component="Delete" /> -->
<img class="w-72px h-72px" src="@/assets/image/zu6302@2x.png">
</div> </div>
<p>批量删除</p> <p>批量删除</p>
</div> </div>
<div class="btn-group"> <!-- <div class="btn-group">
<div class="multi-icon pointer flex-center" @click="onClose"> <div class="multi-icon pointer flex-center" @click="onClose">
<n-icon :size="22" :component="Close" />
</div> </div>
<p>关闭</p> <p>关闭</p>
</div> </div> -->
</div> </div>
<div class="pointer absolute right-150px top-50% translate-y-[-50%]" @click="onClose">
<img class="w-30px h-30px" src="@/assets/image/zu6306@2x.png" alt="">
</div>
</div>
</section> </section>
<ContactModal <ContactModal
@ -126,13 +136,9 @@ const onContactModal = (data: { id: number; type: number }[]) => {
justify-content: center; justify-content: center;
.btn-group { .btn-group {
width: 50px;
height: 80px;
margin: 0 15px;
.multi-icon { .multi-icon {
width: 50px; width: 72px;
height: 50px; height: 72px;
background-color: var(--im-active-bg-color); background-color: var(--im-active-bg-color);
border-radius: 50%; border-radius: 50%;

View File

@ -335,7 +335,7 @@ onMounted(() => {
<im-avatar <im-avatar
class="pointer" class="pointer"
:src="item.avatar" :src="item.avatar"
:size="30" :size="42"
:username="item.nickname" :username="item.nickname"
@click="showUserInfoModal(item.user_id)" @click="showUserInfoModal(item.user_id)"
/> />
@ -351,7 +351,7 @@ onMounted(() => {
> >
<span class="at">@</span>{{ item.nickname }} <span class="at">@</span>{{ item.nickname }}
</span> </span>
<span>{{ parseTime(item.created_at, '{m}/{d} {h}:{i}') }}</span> <span>{{ parseTime(item.created_at, '{y}/{m}/{d} {h}:{i}') }}</span>
</div> </div>
<div <div
@ -420,6 +420,7 @@ onMounted(() => {
:show="dropdown.show" :show="dropdown.show"
:x="dropdown.x" :x="dropdown.x"
:y="dropdown.y" :y="dropdown.y"
style="width: 142px;"
:options="dropdown.options" :options="dropdown.options"
@select="onContextMenuHandle" @select="onContextMenuHandle"
@clickoutside="closeDropdownMenu" @clickoutside="closeDropdownMenu"
@ -489,7 +490,7 @@ onMounted(() => {
} }
.avatar-column { .avatar-column {
width: 35px; width: 47px;
display: flex; display: flex;
align-items: center; align-items: center;
order: 2; order: 2;
@ -524,7 +525,7 @@ onMounted(() => {
.nickname { .nickname {
color: var(--im-text-color); color: var(--im-text-color);
margin-right: 5px; margin-right: 5px;
font-size: 12px;
.at { .at {
display: none; display: none;
} }

View File

@ -37,14 +37,14 @@ export function useMenu() {
dropdown.options.push({ label: '复制', key: 'copy' }) dropdown.options.push({ label: '复制', key: 'copy' })
} }
dropdown.options.push({ label: '多选', key: 'multiSelect' })
dropdown.options.push({ label: '引用', key: 'quote' })
if (isRevoke(uid, item)) { if (isRevoke(uid, item)) {
dropdown.options.push({ label: `撤回`, key: 'revoke' }) dropdown.options.push({ label: `撤回`, key: 'revoke' })
} }
dropdown.options.push({ label: '回复', key: 'quote' })
dropdown.options.push({ label: '删除', key: 'delete' }) dropdown.options.push({ label: '删除', key: 'delete' })
dropdown.options.push({ label: '多选', key: 'multiSelect' })
if ([3, 4, 5].includes(item.msg_type)) { if ([3, 4, 5].includes(item.msg_type)) {
dropdown.options.push({ label: '下载', key: 'download' }) dropdown.options.push({ label: '下载', key: 'download' })

View File

@ -1,16 +1,22 @@
import { defineConfig } from 'unocss' import { defineConfig } from 'unocss'
import { presetAttributify, presetIcons } from 'unocss' import { presetUno, presetAttributify, presetIcons } from 'unocss'
export default defineConfig({ export default defineConfig({
// 预设 // 预设
presets: [ presets: [
presetUno(),
presetAttributify(), // 启用属性模式 presetAttributify(), // 启用属性模式
presetIcons(), // 启用图标 presetIcons(), // 启用图标
], ],
// 自定义规则
rules: [ rules: [
// 通过自定义规则覆盖默认的 container 样式
['container', { 'max-width': 'none' }], // 或者根据需要设置其他样式
],
safelist: [
'container' // 确保 container 在 safelist 中,以便 UnoCSS 忽略它
], ],
// 快捷方式 // 快捷方式
shortcuts: { shortcuts: {
'btn': 'px-4 py-2 rounded-lg bg-blue-500 text-white hover:bg-blue-600', 'uno-container': 'container' // 创建 container 的别名
}, },
}) })

View File

@ -25,11 +25,10 @@ export default defineConfig(({ mode }) => {
plugins: [ plugins: [
vue(), vue(),
vueJsx({}), vueJsx({}),
compressPlugin(), // vueDevTools({
UnoCSS(), // launchEditor:'cursor'
vueDevTools({ // }),
launchEditor: 'cursor', UnoCSS()
})
], ],
define: { define: {
__APP_ENV__: env.APP_ENV __APP_ENV__: env.APP_ENV