处理冲突
Some checks are pending
Check / lint (push) Waiting to run
Check / typecheck (push) Waiting to run
Check / build (build, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build, 18.x, windows-latest) (push) Waiting to run
Check / build (build:app, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build:app, 18.x, windows-latest) (push) Waiting to run
Check / build (build:mp-weixin, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build:mp-weixin, 18.x, windows-latest) (push) Waiting to run

This commit is contained in:
wwt 2025-03-12 14:57:55 +08:00
commit 4be1c7c9a4
30 changed files with 1757 additions and 905 deletions

75
auto-imports.d.ts vendored Normal file
View File

@ -0,0 +1,75 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useDialog: typeof import('naive-ui')['useDialog']
const useId: typeof import('vue')['useId']
const useLoadingBar: typeof import('naive-ui')['useLoadingBar']
const useMessage: typeof import('naive-ui')['useMessage']
const useModel: typeof import('vue')['useModel']
const useNotification: typeof import('naive-ui')['useNotification']
const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

66
components.d.ts vendored Normal file
View File

@ -0,0 +1,66 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
AudioMessage: typeof import('./src/components/talk/message/AudioMessage.vue')['default']
Avatar: typeof import('./src/components/base/Avatar.vue')['default']
AvatarCropper: typeof import('./src/components/base/AvatarCropper.vue')['default']
AvatarModule: typeof import('./src/components/avatar-module/index.vue')['default']
CheckBox: typeof import('./src/components/checkBox/index.vue')['default']
CodeMessage: typeof import('./src/components/talk/message/CodeMessage.vue')['default']
CustomBtn: typeof import('./src/components/custom-btn/custom-btn.vue')['default']
CustomInput: typeof import('./src/components/custom-input/custom-input.vue')['default']
CustomNavbar: typeof import('./src/components/custom-navbar/index.vue')['default']
CustomRefresher: typeof import('./src/components/custom-refresher/custom-refresher.vue')['default']
DeepBubble: typeof import('./src/components/deep-bubble/deep-bubble.vue')['default']
FileMessage: typeof import('./src/components/talk/message/FileMessage.vue')['default']
ForwardMessage: typeof import('./src/components/talk/message/ForwardMessage.vue')['default']
ForwardRecord: typeof import('./src/components/talk/ForwardRecord.vue')['default']
GroupNoticeMessage: typeof import('./src/components/talk/message/GroupNoticeMessage.vue')['default']
HistoryRecord: typeof import('./src/components/talk/HistoryRecord.vue')['default']
ImageMessage: typeof import('./src/components/talk/message/ImageMessage.vue')['default']
LinkMessage: typeof import('./src/components/talk/message/LinkMessage.vue')['default']
Loading: typeof import('./src/components/base/Loading.vue')['default']
LoginMessage: typeof import('./src/components/talk/message/LoginMessage.vue')['default']
Message: typeof import('./src/components/x-message/message/index.vue')['default']
MixedMessage: typeof import('./src/components/talk/message/MixedMessage.vue')['default']
NButton: typeof import('naive-ui')['NButton']
NIcon: typeof import('naive-ui')['NIcon']
PageAnimation: typeof import('./src/components/page-animation/index.vue')['default']
RevokeMessage: typeof import('./src/components/talk/message/RevokeMessage.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SysGroupCancelMutedMessage: typeof import('./src/components/talk/message/system/SysGroupCancelMutedMessage.vue')['default']
SysGroupCreateMessage: typeof import('./src/components/talk/message/system/SysGroupCreateMessage.vue')['default']
SysGroupJoinMessage: typeof import('./src/components/talk/message/system/SysGroupJoinMessage.vue')['default']
SysGroupMemberCancelMutedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberCancelMutedMessage.vue')['default']
SysGroupMemberKickedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberKickedMessage.vue')['default']
SysGroupMemberMutedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberMutedMessage.vue')['default']
SysGroupMemberQuitMessage: typeof import('./src/components/talk/message/system/SysGroupMemberQuitMessage.vue')['default']
SysGroupMutedMessage: typeof import('./src/components/talk/message/system/SysGroupMutedMessage.vue')['default']
SysGroupTransferMessage: typeof import('./src/components/talk/message/system/SysGroupTransferMessage.vue')['default']
SysTextMessage: typeof import('./src/components/talk/message/system/SysTextMessage.vue')['default']
TabbarItem: typeof import('./src/components/x-tabbar/components/tabbar-item/index.vue')['default']
TextMessage: typeof import('./src/components/talk/message/TextMessage.vue')['default']
UnknownMessage: typeof import('./src/components/talk/message/UnknownMessage.vue')['default']
UploadsModal: typeof import('./src/components/base/UploadsModal.vue')['default']
VideoMessage: typeof import('./src/components/talk/message/VideoMessage.vue')['default']
VoteMessage: typeof import('./src/components/talk/message/VoteMessage.vue')['default']
XCalendar: typeof import('./src/components/x-calendar/index.vue')['default']
XCaptcha: typeof import('./src/components/x-captcha/index.vue')['default']
XConfirm: typeof import('./src/components/x-confirm/index.vue')['default']
XDateSelect: typeof import('./src/components/x-date-select/index.vue')['default']
XLoaderror: typeof import('./src/components/x-loaderror/index.vue')['default']
XLoading: typeof import('./src/components/x-loading/index.vue')['default']
XMessage: typeof import('./src/components/x-message/index.vue')['default']
XPaging: typeof import('./src/components/x-paging/index.vue')['default']
XTabbar: typeof import('./src/components/x-tabbar/index.vue')['default']
Xtime: typeof import('./src/components/base/Xtime.vue')['default']
}
}

6
env/.env.dev vendored
View File

@ -5,4 +5,8 @@ VITE_SHOW_CONSOLE = true
# 是否开启sourcemap # 是否开启sourcemap
VITE_SHOW_SOURCEMAP = true VITE_SHOW_SOURCEMAP = true
# baseUrl # baseUrl
VITE_BASEURL = 'http://warehouse.szjixun.cn/oa_backend' VITE_BASEURL = 'http://172.16.100.93:8503'
#VITE_SOCKET_API
VITE_SOCKET_API = 'ws://172.16.100.93:8504'
# EPRAPI baseUrl
VITE_EPR_BASEURL = 'http://114.218.158.24:9020'

4
env/.env.prod vendored
View File

@ -1,8 +1,8 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取 # 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'prod' NODE_ENV = 'prod'
# 是否显示console # 是否显示console
VITE_SHOW_CONSOLE = true VITE_SHOW_CONSOLE = false
# 是否开启sourcemap # 是否开启sourcemap
VITE_SHOW_SOURCEMAP = true VITE_SHOW_SOURCEMAP = false
# baseUrl # baseUrl
VITE_BASEURL = 'https://oa-a.szjixun.cn/api' VITE_BASEURL = 'https://oa-a.szjixun.cn/api'

1
env/.env.test vendored
View File

@ -5,7 +5,6 @@ VITE_SHOW_CONSOLE = true
# 是否开启sourcemap # 是否开启sourcemap
VITE_SHOW_SOURCEMAP = true VITE_SHOW_SOURCEMAP = true
# baseUrl # baseUrl
# VITE_BASEURL = 'https://warehouse.szjixun.cn/oa_backend'
VITE_BASEURL = 'http://172.16.100.93:8503' VITE_BASEURL = 'http://172.16.100.93:8503'
#VITE_SOCKET_API #VITE_SOCKET_API
VITE_SOCKET_API = 'ws://172.16.100.93:8504' VITE_SOCKET_API = 'ws://172.16.100.93:8504'

View File

@ -8,7 +8,9 @@
"test:h5": "uni --mode test --port 2468", "test:h5": "uni --mode test --port 2468",
"prod:h5": "uni --mode prod", "prod:h5": "uni --mode prod",
"build:h5:test": "uni build --mode test", "build:h5:test": "uni build --mode test",
"build:h5:prod": "uni build --mode prod" "build:h5:prod": "uni build --mode prod",
"preview:h5": "uni preview --mode test",
"preview:h5:prod": "uni preview --mode prod"
}, },
"dependencies": { "dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-4000020240111001", "@dcloudio/uni-app": "3.0.0-alpha-4000020240111001",
@ -25,6 +27,7 @@
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-4000020240111001", "@dcloudio/uni-mp-weixin": "3.0.0-alpha-4000020240111001",
"@dcloudio/uni-mp-xhs": "3.0.0-alpha-4000020240111001", "@dcloudio/uni-mp-xhs": "3.0.0-alpha-4000020240111001",
"@dcloudio/uni-quickapp-webview": "3.0.0-alpha-4000020240111001", "@dcloudio/uni-quickapp-webview": "3.0.0-alpha-4000020240111001",
"@icon-park/vue-next": "^1.4.2",
"@uni-helper/axios-adapter": "^1.5.2", "@uni-helper/axios-adapter": "^1.5.2",
"@uni-helper/localforage-adapter": "^1.0.2", "@uni-helper/localforage-adapter": "^1.0.2",
"@uni-helper/uni-use": "^0.19.12", "@uni-helper/uni-use": "^0.19.12",
@ -58,13 +61,16 @@
"@vue/runtime-core": "^3.3.8", "@vue/runtime-core": "^3.3.8",
"@vue/tsconfig": "^0.5.1", "@vue/tsconfig": "^0.5.1",
"lint-staged": "^15.2.0", "lint-staged": "^15.2.0",
"naive-ui": "^2.41.0",
"pinia": "2.0.36", "pinia": "2.0.36",
"sass": "^1.77.8", "sass": "^1.77.8",
"simple-git-hooks": "^2.9.0", "simple-git-hooks": "^2.9.0",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"unocss": "^0.58.9", "unocss": "^0.58.9",
"unocss-applet": "^0.8.2", "unocss-applet": "^0.8.2",
"vite": "^5.0.11", "unplugin-auto-import": "^19.1.1",
"unplugin-vue-components": "^28.4.1",
"vite": "4.5.1",
"vue-tsc": "^1.8.27" "vue-tsc": "^1.8.27"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +1,60 @@
<script setup> <script setup>
import {useStatus} from "@/store/status"; import { useStatus } from '@/store/status'
import { useUserStore } from '@/store' import { useUserStore } from '@/store'
import { import { useProvideUserModal } from '@/hooks'
useProvideUserModal, import {useAuth} from "@/store/auth";
} from '@/hooks' const {token} = useAuth()
import ws from '@/connect' import ws from '@/connect'
const {statusBarHeight}= useStatus() const { statusBarHeight } = useStatus()
const { uid, isShow } = useProvideUserModal() const { uid, isShow } = useProvideUserModal()
const userStore = useUserStore() const userStore = useUserStore()
const root = document.documentElement const root = document.documentElement
root.style.setProperty('--statusBarHeight',`${statusBarHeight.value}px`) root.style.setProperty('--statusBarHeight', `${statusBarHeight.value}px`)
const handleWebview = () => {
let statusBarHeight = window?.plus?.navigator?.getStatusbarHeight()
const webview = plus.webview.currentWebview()
webview.setStyle({
top: statusBarHeight,
bottom: 0,
})
// console.log(webview)
token.value = webview.token
}
const init = () => { const init = () => {
userStore.loadSetting() userStore.loadSetting()
ws.connect() ws.connect()
if (typeof plus !== 'undefined') {
handleWebview()
} else {
document.addEventListener('plusready', () => {
handleWebview()
})
}
} }
init() init()
</script> </script>
<style lang="scss"> <style lang="scss">
@import "@/static/css/color.scss"; @import '@/static/css/color.scss';
@import "@/static/css/font.scss"; @import '@/static/css/font.scss';
/* #ifdef APP-NVUE */ /* #ifdef APP-NVUE */
@import '@/uni_modules/tmui/scss/nvue.css'; @import '@/uni_modules/tmui/scss/nvue.css';
/* #endif */ /* #endif */
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
@import '@/uni_modules/tmui/scss/noNvue.css'; @import '@/uni_modules/tmui/scss/noNvue.css';
/* #endif */ /* #endif */
*{ * {
box-sizing: border-box; box-sizing: border-box;
} }
/*解决阅览图片关闭按钮会显示在状态栏区域的问题*/ /*解决阅览图片关闭按钮会显示在状态栏区域的问题*/
#u-a-p>div>div{ #u-a-p > div > div {
margin-top:var(--statusBarHeight) margin-top: var(--statusBarHeight);
} }
/*不显示滚动条的类*/ /*不显示滚动条的类*/
.no-scroll { .no-scroll {
-ms-overflow-style: none; /* IE 和 Edge */ -ms-overflow-style: none; /* IE 和 Edge */
scrollbar-width: none; /* Firefox */ scrollbar-width: none; /* Firefox */
} }
.no-scroll::-webkit-scrollbar { .no-scroll::-webkit-scrollbar {
display: none; /* Webkit 浏览器 */ display: none; /* Webkit 浏览器 */
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="custom-search-input"> <div class="custom-input">
<tm-input <tm-input
class="search-input" class="search-input"
placeholder="请输入…" placeholder="请输入…"
@ -13,7 +13,9 @@
:showClear="true" :showClear="true"
@clear="clearInput" @clear="clearInput"
placeholderStyle="color:#BABABA" placeholderStyle="color:#BABABA"
:disabled="props?.disabled"
></tm-input> ></tm-input>
<div v-if="props?.disabled" class="custom-input-disabled"></div>
</div> </div>
</template> </template>
<script setup> <script setup>
@ -21,6 +23,7 @@ import { defineProps, defineEmits, reactive, watch } from 'vue'
const props = defineProps({ const props = defineProps({
searchText: String, searchText: String,
first_talk_record_infos: Object, first_talk_record_infos: Object,
disabled: Boolean,
}) })
const state = reactive({ const state = reactive({
searchText: '', // searchText: '', //
@ -45,18 +48,27 @@ const inputSearchText = (e) => {
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.custom-search-input { .custom-input {
width: 100%; width: 100%;
position: relative;
.search-input { .search-input {
width: 100%; width: 100%;
} }
::v-deep .noNvueBorder > .noNvueBorder > .noNvueBorder { ::v-deep .noNvueBorder > .noNvueBorder > .noNvueBorder {
background: #f9f9fd !important; background: #f9f9fd !important;
border-radius: 8rpx !important; border-radius: 8rpx !important;
} }
.search-input::v-deep .tmicon-times-circle-fill::before { .search-input::v-deep .tmicon-times-circle-fill::before {
content: '\e82a'; content: '\e82a';
color: #d2d2d5; color: #d2d2d5;
} }
.custom-input-disabled {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
} }
</style> </style>

View File

@ -5,6 +5,7 @@
:title="props.title" :title="props.title"
:shadow="props.shadowNum" :shadow="props.shadowNum"
:fontSize="34" :fontSize="34"
:showStatusBar="false"
> >
<template #left> <template #left>
<slot name="left"></slot> <slot name="left"></slot>

View File

@ -1,91 +1,95 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive } from 'vue' import { ref, reactive, onMounted } from "vue";
import { PlayOne, PauseOne } from '@icon-park/vue-next' import { PlayOne, PauseOne } from "@icon-park/vue-next";
import { ITalkRecordExtraAudio, ITalkRecord } from '@/types/chat' import { ITalkRecordExtraAudio, ITalkRecord } from "@/types/chat";
defineProps<{ const props = defineProps<{
extra: ITalkRecordExtraAudio extra: ITalkRecordExtraAudio;
data: ITalkRecord data: ITalkRecord;
maxWidth?: Boolean maxWidth?: Boolean;
}>() }>();
const audioRef = ref() const audioRef = ref();
const audioContext = ref<any>(null);
const durationDesc = ref('-') const durationDesc = ref("-");
const state = reactive({ const state = reactive({
isAudioPlay: false, isAudioPlay: false,
progress: 0, progress: 0,
duration: 0, duration: 0,
currentTime: 0, currentTime: 0,
loading: true loading: true,
}) });
onMounted(() => {
// 使uni-app
audioContext.value = uni.createInnerAudioContext();
audioContext.value.src = props.extra.url;
audioContext.value.onCanplay(() => {
state.duration = audioContext.value.duration;
durationDesc.value = formatTime(parseInt(audioContext.value.duration));
state.loading = false;
});
audioContext.value.onTimeUpdate(() => {
if (audioContext.value.duration == 0) {
state.progress = 0;
} else {
state.currentTime = audioContext.value.currentTime;
state.progress =
(audioContext.value.currentTime / audioContext.value.duration) * 100;
}
});
audioContext.value.onEnded(() => {
state.isAudioPlay = false;
state.progress = 0;
});
audioContext.value.onError((e) => {
console.log("音频播放异常===>", e);
});
});
const onPlay = () => { const onPlay = () => {
if (state.isAudioPlay) { if (state.isAudioPlay) {
audioRef.value.pause() audioContext.value.pause();
} else { } else {
audioRef.value.play() audioContext.value.play();
} }
state.isAudioPlay = !state.isAudioPlay state.isAudioPlay = !state.isAudioPlay;
} };
const onPlayEnd = () => { const onPlayEnd = () => {
state.isAudioPlay = false state.isAudioPlay = false;
state.progress = 0 state.progress = 0;
} };
const onCanplay = () => {
state.duration = audioRef.value.duration
durationDesc.value = formatTime(parseInt(audioRef.value.duration))
state.loading = false
}
const onError = (e: any) => {
console.log('音频播放异常===>', e)
}
const onTimeUpdate = () => {
let audio = audioRef.value
if (audio.duration == 0) {
state.progress = 0
} else {
state.currentTime = audio.currentTime
state.progress = (audio.currentTime / audio.duration) * 100
}
}
const formatTime = (value: number = 0) => { const formatTime = (value: number = 0) => {
if (value == 0) { if (value == 0) {
return '-' return "-";
} }
const minutes = Math.floor(value / 60) const minutes = Math.floor(value / 60);
let seconds = value let seconds = value;
if (minutes > 0) { if (minutes > 0) {
seconds = Math.floor(value - minutes * 60) seconds = Math.floor(value - minutes * 60);
} }
return `${minutes}'${seconds}"` return `${minutes}'${seconds}"`;
} };
</script> </script>
<template> <template>
<div class="im-message-audio"> <div class="im-message-audio">
<audio
ref="audioRef"
preload="auto"
type="audio/mp3,audio/wav"
:src="extra.url"
@timeupdate="onTimeUpdate"
@ended="onPlayEnd"
@canplay="onCanplay"
@error="onError"
/>
<div class="play"> <div class="play">
<div class="btn pointer" @click.stop="onPlay"> <div class="btn pointer" @click.stop="onPlay">
<n-icon :size="18" :component="state.isAudioPlay ? PauseOne : PlayOne" /> <n-icon
:size="18"
:component="state.isAudioPlay ? PauseOne : PlayOne"
/>
</div> </div>
</div> </div>
<div class="desc"> <div class="desc">
@ -241,7 +245,7 @@ const formatTime = (value: number = 0) => {
} }
} }
html[theme-mode='dark'] { html[theme-mode="dark"] {
.im-message-audio { .im-message-audio {
--audio-bg-color: #2c2c32; --audio-bg-color: #2c2c32;
--audio-btn-bg-color: rgb(78, 75, 75); --audio-btn-bg-color: rgb(78, 75, 75);

View File

@ -1,33 +1,32 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import { ref } from "vue";
import { NCode } from 'naive-ui' import { NCode, useMessage } from "naive-ui";
import { Copy, Stretching } from '@icon-park/vue-next' import { Copy, Stretching } from "@icon-park/vue-next";
import { clipboard } from '@/utils/common' import { clipboard } from "@/utils/common";
import { useUtil } from '@/hooks' import { ITalkRecordExtraCode, ITalkRecord } from "@/types/chat";
import { ITalkRecordExtraCode, ITalkRecord } from '@/types/chat'
const props = defineProps<{ const props = defineProps<{
extra: ITalkRecordExtraCode extra: ITalkRecordExtraCode;
data: ITalkRecord data: ITalkRecord;
maxWidth?: Boolean maxWidth?: Boolean;
}>() }>();
const { useMessage } = useUtil() const message = useMessage();
const lineMumber = props.extra.code.trim().split('\n').length const lineMumber = props.extra.code.trim().split("\n").length;
const full = ref(false) const full = ref(false);
const onClipboard = () => { const onClipboard = () => {
clipboard(props.extra.code, () => { clipboard(props.extra.code, () => {
useMessage.success('复制成功') message.success("复制成功");
}) });
} };
</script> </script>
<template> <template>
<section <section
class="im-message-code el-container is-vertical" class="im-message-code el-container is-vertical"
:class="{ :class="{
maxwidth: maxWidth, maxwidth: maxWidth,
full: full full: full,
}" }"
> >
<header class="el-header tools"> <header class="el-header tools">
@ -37,9 +36,16 @@ const onClipboard = () => {
<n-icon class="icon" :component="Copy" @click="onClipboard" /> <n-icon class="icon" :component="Copy" @click="onClipboard" />
</p> </p>
</header> </header>
<main class="el-main me-scrollbar me-scrollbar-thumb" :lineMumber="lineMumber"> <main
class="el-main me-scrollbar me-scrollbar-thumb"
:lineMumber="lineMumber"
>
<n-code :language="extra.lang" :code="extra.code" show-line-numbers /> <n-code :language="extra.lang" :code="extra.code" show-line-numbers />
<div class="el-footer mask pointer" v-show="lineMumber > 20" @click="full = !full"> <div
class="el-footer mask pointer"
v-show="lineMumber > 20"
@click="full = !full"
>
查看更多 查看更多
</div> </div>
</main> </main>
@ -110,17 +116,25 @@ const onClipboard = () => {
position: sticky; position: sticky;
bottom: 0; bottom: 0;
left: 0; left: 0;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%); background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 1) 100%
);
color: var(--im-text-color); color: var(--im-text-color);
} }
} }
html[theme-mode='dark'] { html[theme-mode="dark"] {
.im-message-code { .im-message-code {
background: var(--im-message-bg-color); background: var(--im-message-bg-color);
.mask { .mask {
background: linear-gradient(to bottom, transparent 0%, var(--im-bg-color) 100%); background: linear-gradient(
to bottom,
transparent 0%,
var(--im-bg-color) 100%
);
} }
} }
} }

View File

@ -20,7 +20,8 @@ let show = ref(false)
}" }"
> >
<div class="title"> <div class="title">
<n-tag :bordered="false" size="small" type="primary">群公告</n-tag> <!-- <n-tag :bordered="false" size="small" type="primary">群公告</n-tag> -->
<text>群公告</text>
<!-- {{ extra.title }} --> <!-- {{ extra.title }} -->
</div> </div>
<div class="title" :class="{ ellipsis: !show }"> <div class="title" :class="{ ellipsis: !show }">

View File

@ -0,0 +1,95 @@
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<!-- 完全复制的textMessage组件没有用处仅兜底真有14类型时的场景后续会单独做制作分享卡片功能到时再根据分享卡片样式重做本页面 -->
<script lang="ts" setup>
import { textReplaceEmoji } from '@/utils/emojis'
import { textReplaceLink, textReplaceMention } from '@/utils/strings'
import { ITalkRecordExtraText, ITalkRecord } from '@/types/chat'
const props = defineProps<{
extra: ITalkRecordExtraText
data: ITalkRecord
maxWidth?: boolean
source?: 'panel' | 'forward' | 'history'
}>()
const float = props.data.float
let textContent = props.extra?.content || ''
textContent = textReplaceLink(textContent)
if (props.data.talk_type == 2) {
textContent = textReplaceMention(textContent, '#1890ff')
}
textContent = textReplaceEmoji(textContent)
</script>
<template>
<div
class="im-message-text"
:class="{
left: float == 'left',
right: float == 'right',
maxwidth: maxWidth,
'radius-reset': source != 'panel',
}"
>
<pre v-html="textContent" />
</div>
</template>
<style lang="less" scoped>
.im-message-text {
min-width: 40rpx;
min-height: 40rpx;
padding: 22rpx 30rpx;
color: #1a1a1a;
background: #ffffff;
border-radius: 0 16rpx 16rpx 16rpx;
&.right {
background-color: #46299d;
color: #ffffff;
border-radius: 16rpx 0 16rpx 16rpx;
}
&.maxwidth {
max-width: 486rpx;
}
&.radius-reset {
border-radius: 0;
}
pre {
white-space: pre-wrap;
overflow: hidden;
word-break: break-word;
word-wrap: break-word;
font-size: 32rpx;
font-family: 'PingFang SC', 'Microsoft YaHei', 'Alibaba PuHuiTi 2.0 45';
line-height: 44rpx;
:deep(.emoji) {
vertical-align: text-bottom;
margin: 0 10rpx;
width: 44rpx;
height: 44rpx;
}
:deep(a) {
color: #2196f3;
text-decoration: revert;
}
}
}
</style>

View File

@ -33,6 +33,10 @@ defineProps({
"{{ nickname }}" 撤回了一条消息 | "{{ nickname }}" 撤回了一条消息 |
{{ formatTime(datetime) }} {{ formatTime(datetime) }}
</span> </span>
<!-- <span v-if="login_uid == user_idA"> 你撤回B了一条消息 | {{ formatTime(datetime) }} </span>
<span v-else-if="login_uid == user_idB"> A撤回你了一条消息 | {{ formatTime(datetime) }} </span>
<span v-else> A撤回B了一条消息 | {{ formatTime(datetime) }} </span> -->
</div> </div>
</div> </div>
</template> </template>

View File

@ -30,6 +30,7 @@ class Connect {
}, },
// Websocket 连接成功回调方法 // Websocket 连接成功回调方法
onOpen: () => { onOpen: () => {
console.log("socket已连接")
// 更新 WebSocket 连接状态 // 更新 WebSocket 连接状态
useUserStore().updateSocketStatus(true) useUserStore().updateSocketStatus(true)
// online.value = true; // online.value = true;
@ -37,6 +38,7 @@ class Connect {
}, },
// Websocket 断开连接回调方法 // Websocket 断开连接回调方法
onClose: () => { onClose: () => {
console.log("socket已断开")
// 更新 WebSocket 连接状态 // 更新 WebSocket 连接状态
useUserStore().updateSocketStatus(false) useUserStore().updateSocketStatus(false)
// online.value = false // online.value = false

View File

@ -11,6 +11,7 @@ export const ChatMsgTypeLogin = 10 // 登录消息
export const ChatMsgTypeVote = 11 // 投票消息 export const ChatMsgTypeVote = 11 // 投票消息
export const ChatMsgTypeMixed = 12 // 混合消息 export const ChatMsgTypeMixed = 12 // 混合消息
export const ChatMsgTypeGroupNotice = 13 // 群公告消息 export const ChatMsgTypeGroupNotice = 13 // 群公告消息
export const ChatMsgTypeLink = 14 // 链接消息
export const ChatMsgSysText = 1000 // 系统文本消息 export const ChatMsgSysText = 1000 // 系统文本消息
export const ChatMsgSysGroupCreate = 1101 // 创建群聊消息 export const ChatMsgSysGroupCreate = 1101 // 创建群聊消息
@ -40,6 +41,7 @@ export const ChatMsgTypeMapping = {
[ChatMsgTypeCode]: '[代码消息]', [ChatMsgTypeCode]: '[代码消息]',
[ChatMsgTypeMixed]: '[图文消息]', [ChatMsgTypeMixed]: '[图文消息]',
[ChatMsgTypeGroupNotice]: '[群公告]', [ChatMsgTypeGroupNotice]: '[群公告]',
[ChatMsgTypeLink]: '[链接]',
[ChatMsgSysText]: '[系统消息]', [ChatMsgSysText]: '[系统消息]',
[ChatMsgSysGroupCreate]: '[创建群消息]', [ChatMsgSysGroupCreate]: '[创建群消息]',
[ChatMsgSysGroupMemberJoin]: '[加入群消息]', [ChatMsgSysGroupMemberJoin]: '[加入群消息]',
@ -69,6 +71,7 @@ export const MessageComponents = {
[ChatMsgTypeCode]: 'code-message', [ChatMsgTypeCode]: 'code-message',
[ChatMsgTypeMixed]: 'mixed-message', [ChatMsgTypeMixed]: 'mixed-message',
[ChatMsgTypeGroupNotice]: 'group-notice-message', [ChatMsgTypeGroupNotice]: 'group-notice-message',
[ChatMsgTypeLink]: 'link-message',
[ChatMsgSysText]: 'sys-text-message', [ChatMsgSysText]: 'sys-text-message',
[ChatMsgSysGroupCreate]: 'sys-group-create-message', [ChatMsgSysGroupCreate]: 'sys-group-create-message',
[ChatMsgSysGroupMemberJoin]: 'sys-group-join-message', [ChatMsgSysGroupMemberJoin]: 'sys-group-join-message',
@ -92,5 +95,6 @@ export const ForwardableMessageType = [
ChatMsgTypeVideo, ChatMsgTypeVideo,
ChatMsgTypeFile, ChatMsgTypeFile,
ChatMsgTypeLocation, ChatMsgTypeLocation,
ChatMsgTypeCard ChatMsgTypeCard,
ChatMsgTypeLink
] ]

View File

@ -7,25 +7,29 @@ import lodash from 'lodash'
export const useTalkRecord = (uid) => { export const useTalkRecord = (uid) => {
const dialogueStore = useDialogueStore() const dialogueStore = useDialogueStore()
const { getDialogueList,addDialogueRecord,zpagingComplete } = useDialogueListStore() const {
getDialogueList,
addDialogueRecord,
zpagingComplete,
} = useDialogueListStore()
const records = computed(() => { const records = computed(() => {
const dialogueList = getDialogueList(dialogueStore.index_name) const dialogueList = getDialogueList(dialogueStore.index_name)
if(dialogueList){ if (dialogueList) {
return lodash.cloneDeep(dialogueList.records).reverse() return lodash.cloneDeep(dialogueList.records).reverse()
} }
return lodash.cloneDeep(dialogueStore.records)?.reverse() return lodash.cloneDeep(dialogueStore.records)?.reverse()
} ) })
const location = reactive({ const location = reactive({
msgid: '', msgid: '',
num: 0 num: 0,
}) })
const loadConfig = reactive({ const loadConfig = reactive({
receiver_id: 0, receiver_id: 0,
talk_type: 0, talk_type: 0,
status: 0, status: 0,
cursor: 0 cursor: 0,
}) })
const onJumpMessage = (msgid) => { const onJumpMessage = (msgid) => {
@ -49,7 +53,7 @@ export const useTalkRecord = (uid) => {
return el?.scrollTo({ return el?.scrollTo({
top: 0, top: 0,
behavior: 'smooth' behavior: 'smooth',
}) })
} }
@ -57,7 +61,7 @@ export const useTalkRecord = (uid) => {
location.num = 0 location.num = 0
element?.scrollIntoView({ element?.scrollIntoView({
behavior: 'smooth' behavior: 'smooth',
}) })
addClass(element, 'border') addClass(element, 'border')
@ -70,7 +74,7 @@ export const useTalkRecord = (uid) => {
// 加载数据列表 // 加载数据列表
const load = async (params) => { const load = async (params) => {
const request = { const request = {
limit:30, limit: 30,
...params, ...params,
talk_type: params.talk_type, talk_type: params.talk_type,
receiver_id: params.receiver_id, receiver_id: params.receiver_id,
@ -81,9 +85,9 @@ export const useTalkRecord = (uid) => {
let scrollHeight = 0 let scrollHeight = 0
const el = document.getElementById('imChatPanel') const el = document.getElementById('imChatPanel')
// if (el) { if (el) {
// scrollHeight = el.scrollHeight scrollHeight = el.scrollHeight
// } }
const { data, code } = await ServeTalkRecords(request) const { data, code } = await ServeTalkRecords(request)
if (code != 200) { if (code != 200) {
return (loadConfig.status = 1) return (loadConfig.status = 1)
@ -104,10 +108,14 @@ export const useTalkRecord = (uid) => {
const reverseItems = lodash.cloneDeep(items).reverse() const reverseItems = lodash.cloneDeep(items).reverse()
dialogueStore.unshiftDialogueRecord(reverseItems) dialogueStore.unshiftDialogueRecord(reverseItems)
addDialogueRecord(params.direction=='down'?reverseItems:items,params.direction=='down'?'add':'unshift') addDialogueRecord(
zpagingComplete(dialogueStore.index_name) params.direction == 'down' ? reverseItems : items,
params.direction == 'down' ? 'add' : 'unshift',
)
zpagingComplete(dialogueStore.index_name, reverseItems)
const dialogueList = getDialogueList(dialogueStore.index_name) const dialogueList = getDialogueList(dialogueStore.index_name)
loadConfig.status = dialogueList.records?.[0]?.sequence > 1 ? 1 : 2 loadConfig.status =
dialogueList.records?.[0]?.sequence > 1 && data?.items?.length > 0 ? 1 : 2
// loadConfig.cursor = data.cursor // loadConfig.cursor = data.cursor
nextTick(() => { nextTick(() => {
@ -133,17 +141,19 @@ export const useTalkRecord = (uid) => {
const onRefreshLoad = () => { const onRefreshLoad = () => {
let dialogueList = getDialogueList(dialogueStore.index_name) let dialogueList = getDialogueList(dialogueStore.index_name)
if (dialogueList.records[0].sequence === 1 ) { if (dialogueList.records[0].sequence === 1) {
return false return false
} }
if (loadConfig.status == 1) { if (loadConfig.status == 1) {
let filterList = dialogueList.records.filter(item=>item.sequence !== -1) let filterList = dialogueList.records.filter(
(item) => item.sequence !== -1,
)
loadConfig.cursor = filterList[0].sequence loadConfig.cursor = filterList[0].sequence
load({ load({
receiver_id: loadConfig.receiver_id, receiver_id: loadConfig.receiver_id,
talk_type: loadConfig.talk_type, talk_type: loadConfig.talk_type,
direction:'up', direction: 'up',
no_limit:0, no_limit: 0,
}) })
} }
} }
@ -152,12 +162,14 @@ export const useTalkRecord = (uid) => {
let dialogueList = getDialogueList(params.index_name) let dialogueList = getDialogueList(params.index_name)
if (!dialogueList) { if (!dialogueList) {
loadConfig.cursor = 0 loadConfig.cursor = 0
}else{ } else {
let filterList = dialogueList.records.filter(item=>item.sequence !== -1) let filterList = dialogueList.records.filter(
if(params.direction=='up'){ (item) => item.sequence !== -1,
)
if (params.direction == 'up') {
loadConfig.cursor = filterList?.[0]?.sequence loadConfig.cursor = filterList?.[0]?.sequence
}else{ } else {
loadConfig.cursor = filterList?.[filterList.length-1]?.sequence loadConfig.cursor = filterList?.[filterList.length - 1]?.sequence
} }
} }
loadConfig.receiver_id = params.receiver_id loadConfig.receiver_id = params.receiver_id

View File

@ -16,7 +16,7 @@ import pageAnimation from '@/components/page-animation/index.vue'
import * as plugins from './plugins' import * as plugins from './plugins'
const { showMessage } = messagePopup() const { showMessage } = messagePopup()
dayjs.locale('zh-cn') dayjs.locale('zh-cn')
if (import.meta.env.VITE_SHOW_CONSOLE) { if (import.meta.env.VITE_SHOW_CONSOLE === 'true') {
new VConsole() new VConsole()
} }
export function createApp() { export function createApp() {

View File

@ -4,12 +4,14 @@
<div class="item-main-label"> <div class="item-main-label">
<span class="text-[32rpx] font-regular">{{ props?.item?.label }}</span> <span class="text-[32rpx] font-regular">{{ props?.item?.label }}</span>
</div> </div>
<div class="item-main-value" > <div class="item-main-value" @click="toManagePage(props?.item)">
<span class="text-[32rpx] font-regular" v-if="props?.item?.value && props?.item?.value.length < 29"> <span
class="text-[32rpx] font-regular"
v-if="props?.item?.value && props?.item?.value.length < 29"
>
{{ props?.item?.value }} {{ props?.item?.value }}
</span> </span>
<img <img
@click="toManagePage(props?.item)"
v-if=" v-if="
props?.item?.hasPointer && props?.item?.hasPointer &&
(props?.isManager || (props?.isManager ||
@ -31,7 +33,10 @@
></tm-switch> ></tm-switch>
</div> </div>
</div> </div>
<div class="item-sub" v-if="props?.item?.value && props?.item?.value.length > 28"> <div
class="item-sub"
v-if="props?.item?.value && props?.item?.value.length > 28"
>
<span class="text-[32rpx] font-regular">{{ props?.item?.value }}</span> <span class="text-[32rpx] font-regular">{{ props?.item?.value }}</span>
</div> </div>
<div class="item-sub cab1" v-if="props?.item?.subValue"> <div class="item-sub cab1" v-if="props?.item?.subValue">
@ -65,7 +70,14 @@ const props = defineProps({
}) })
const emits = defineEmits(['toManagePage', 'changeSwitch']) const emits = defineEmits(['toManagePage', 'changeSwitch'])
const toManagePage = (item) => { const toManagePage = (item) => {
emits('toManagePage', item.label) if (
props?.item?.hasPointer &&
(props?.isManager ||
(!props?.isManager &&
props?.item?.label !== t('chat.settings.groupName')))
) {
emits('toManagePage', item.label)
}
} }
const modelValue = computed(() => { const modelValue = computed(() => {
let switchStatus = false let switchStatus = false
@ -115,6 +127,7 @@ const changeSwitch = (e, item) => {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
padding: 0 0 0 20rpx;
span { span {
line-height: 44rpx; line-height: 44rpx;
color: #747474; color: #747474;
@ -135,7 +148,7 @@ const changeSwitch = (e, item) => {
color: #747474; color: #747474;
} }
} }
.cab1{ .cab1 {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
max-width: 100%; max-width: 100%;

View File

@ -21,12 +21,15 @@
<div class="base-info"> <div class="base-info">
<div class="base-info-name"> <div class="base-info-name">
<span class="text-[32rpx] font-medium">{{ groupName }}</span> <span class="text-[32rpx] font-medium">{{ groupName }}</span>
<span class="base-info_num text-[32rpx] font-medium"> <span
class="base-info_num text-[32rpx] font-medium"
v-if="groupNum"
>
{{ '' + groupNum + '' }} {{ '' + groupNum + '' }}
</span> </span>
</div> </div>
<div <div
v-if="groupParams?.groupInfo?.group_type !== 1" v-if="groupParams?.groupInfo?.group_type !== 1 && groupType"
class="base-info-tag" class="base-info-tag"
:style="{ :style="{
borderColor: borderColor:
@ -93,7 +96,7 @@
</div> </div>
</div> </div>
<div class="chat-records-search chat-settings-card"> <div class="chat-records-search chat-settings-card">
<customInput></customInput> <customInput :disabled="true"></customInput>
<div class="record-search-types"> <div class="record-search-types">
<div <div
class="record-search-types-each" class="record-search-types-each"
@ -292,10 +295,10 @@ onMounted(() => {
value: t('record.searchType.files'), value: t('record.searchType.files'),
typeIcon: recordSearchTypeIcon_files, typeIcon: recordSearchTypeIcon_files,
}, },
{ // {
value: t('record.searchType.link'), // value: t('record.searchType.link'),
typeIcon: recordSearchTypeIcon_link, // typeIcon: recordSearchTypeIcon_link,
}, // },
] ]
if (dialogueParams.type === 2) { if (dialogueParams.type === 2) {
state.recordSearchTypeList.unshift({ state.recordSearchTypeList.unshift({

View File

@ -27,7 +27,10 @@
<span class="text-[40rpx] font-medium user-info-name"> <span class="text-[40rpx] font-medium user-info-name">
{{ state?.userInfo?.nickname }} {{ state?.userInfo?.nickname }}
</span> </span>
<span class="text-[28rpx] font-medium user-info-job-num"> <span
class="text-[28rpx] font-medium user-info-job-num"
v-if="state?.userInfo?.job_num"
>
{{ $t('user.info.jobNum') + '' + state?.userInfo?.job_num }} {{ $t('user.info.jobNum') + '' + state?.userInfo?.job_num }}
</span> </span>
</div> </div>

View File

@ -2,19 +2,17 @@
<div class="dialog-page"> <div class="dialog-page">
<ZPaging <ZPaging
use-chat-record-mode use-chat-record-mode
:refresher-enabled="false" use-virtual-list
cell-height-mode="dynamic"
:refresher-enabled="true"
:show-scrollbar="false" :show-scrollbar="false"
:loading-more-enabled="false" :loading-more-enabled="true"
:hide-empty-view="true" :hide-empty-view="true"
height="100%" height="100%"
ref="zpagingRef" ref="zpagingRef"
:use-virtual-list="true" v-model="virtualList"
:preload-page="1"
cell-height-mode="dynamic"
virtual-scroll-fps="80"
:loading-more-custom-style="{ display: 'none', height: '0' }" :loading-more-custom-style="{ display: 'none', height: '0' }"
@virtualListChange="virtualListChange" @scrolltolower="onScrollToLower"
@scrolltolower="onRefreshLoad"
> >
<template #top> <template #top>
<customNavbar :title="talkParams.username"> <customNavbar :title="talkParams.username">
@ -39,13 +37,13 @@
<!-- <template #top> <!-- <template #top>
<div class="load-toolbar pointer"> <div class="load-toolbar pointer">
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span> <span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
<span v-else-if="loadConfig.status == 1" @click="onRefreshLoad"> 查看更多消息 ... </span> <span v-else-if="loadConfig.status == 1" @click="onScrollToLower"> 查看更多消息 ... </span>
<span v-else class="no-more"> 没有更多消息了 </span> <span v-else class="no-more"> 没有更多消息了 </span>
</div> </div>
</template> --> </template> -->
<!-- 数据加载状态栏 --> <!-- 数据加载状态栏 -->
<div class="dialog-list"> <div class="dialog-list" @touchstart="handleHidePanel">
<div <div
class="message-item" class="message-item"
v-for="item in virtualList" v-for="item in virtualList"
@ -170,17 +168,26 @@
</div> </div>
<div class="load-toolbar pointer" style="transform: scaleY(-1);"> <div class="load-toolbar pointer" style="transform: scaleY(-1);">
<span v-if="loadConfig.status == 0">正在加载数据中 ...</span> <span v-if="loadConfig.status == 0">正在加载数据中 ...</span>
<span v-else-if="loadConfig.status == 1" @click="onRefreshLoad"> <span v-else-if="loadConfig.status == 1" @click="onScrollToLower">
查看更多消息 ... 查看更多消息 ...
</span> </span>
<span v-else class="no-more">没有更多消息了</span> <span
v-else-if="
loadConfig.status != 0 &&
loadConfig.status != 1 &&
state.localPageLoadDone
"
class="no-more"
>
没有更多消息了
</span>
</div> </div>
</div> </div>
<template #bottom> <template #bottom>
<div class="footBox"> <div class="footBox">
<div v-if="!dialogueStore.isOpenMultiSelect"> <div v-if="!dialogueStore.isOpenMultiSelect">
<div <div
class="pt-[16rpx] ml-[32rpx] mr-[32rpx] flex items-center justify-between" class="pt-[16rpx] ml-[32rpx] mr-[32rpx] flex items-start justify-between"
> >
<div class="flex-1 quillBox"> <div class="flex-1 quillBox">
<QuillEditor <QuillEditor
@ -188,37 +195,69 @@
id="editor" id="editor"
:options="editorOption" :options="editorOption"
@editorChange="onEditorChange" @editorChange="onEditorChange"
style="height: 100%; border: none;" style="width: 100%; flex: 1; height: 100%; border: none;"
@click="onEditorClick"
/> />
<!-- <tm-input type=textarea autoHeight focusColor="#F9F9F9" color="#F9F9F9" :inputPadding="[12]" <!-- <tm-input type=textarea autoHeight focusColor="#F9F9F9" color="#F9F9F9" :inputPadding="[12]"
placeholder=""></tm-input> --> placeholder=""></tm-input> -->
<div class="quote-area" v-if="state?.quoteInfo">
<span
v-if="state?.quoteInfo?.msg_type === 1"
class="text-[28rpx] text-[#999]"
>
{{
state?.quoteInfo?.nickname +
'' +
state?.quoteInfo?.extra?.content
}}
</span>
<span
v-if="state?.quoteInfo?.msg_type === 3"
class="text-[28rpx] text-[#999]"
>
{{
state?.quoteInfo?.nickname +
'' +
'[' +
$t('msg.type') +
']'
}}
</span>
<img
@click="clearQuoteInfo"
style="width: 30rpx; height: 30rpx;"
src="/src/static/image/login/check-circle-filled@3x.png"
/>
</div>
</div>
<div class="flex items-center justify-end h-[72rpx]">
<tm-image
:margin="[10, 0]"
@click="handleEmojiPanel"
:width="52"
:height="52"
:round="12"
:src="state.isOpenEmojiPanel ? keyboard : smile"
></tm-image>
<tm-image
@click="handleFilePanel"
:margin="[10, 0]"
:width="52"
:height="52"
:round="12"
:src="addCircleGray"
></tm-image>
<tm-button
@click="onSendMessageClick"
:margin="[0, 0]"
:padding="[0, 30]"
color="#46299D"
:fontSize="28"
size="mini"
:shadow="0"
label="发送"
></tm-button>
</div> </div>
<tm-image
:margin="[10, 0]"
@click="handleEmojiPanel"
:width="52"
:height="52"
:round="12"
:src="state.isOpenEmojiPanel ? keyboard : smile"
></tm-image>
<tm-image
@click="handleFilePanel"
:margin="[10, 0]"
:width="52"
:height="52"
:round="12"
:src="addCircleGray"
></tm-image>
<tm-button
@click="onSendMessageClick"
:margin="[0, 0]"
:padding="[0, 30]"
color="#46299D"
:fontSize="28"
size="mini"
:shadow="0"
label="发送"
></tm-button>
</div> </div>
<div v-if="state.isOpenEmojiPanel" class="mt-[50rpx]"> <div v-if="state.isOpenEmojiPanel" class="mt-[50rpx]">
<emojiPanel @on-select="onEmoticonEvent" /> <emojiPanel @on-select="onEmoticonEvent" />
@ -392,11 +431,13 @@ const state = ref({
showWin: false, showWin: false,
onfocusItem: null, onfocusItem: null,
sessionId: '', sessionId: '',
localPageLoadDone: true, //
quoteInfo: null, //
}) })
uniOnload((options) => { uniOnload((options) => {
if (options.sessionId) { if (options.sessionId) {
state.sessionId = options.sessionId state.value.sessionId = options.sessionId
} }
}) })
@ -409,7 +450,18 @@ const handleFilePanel = () => {
state.value.isOpenFilePanel = !state.value.isOpenFilePanel state.value.isOpenFilePanel = !state.value.isOpenFilePanel
} }
const onSendMessage = (data = {}) => { ///
const handleHidePanel = () => {
state.value.isOpenFilePanel = false
state.value.isOpenEmojiPanel = false
}
//
const onEditorClick = () => {
handleHidePanel()
}
const onSendMessage = (data = {}, callBack) => {
let message = { let message = {
...data, ...data,
receiver: { receiver: {
@ -421,7 +473,9 @@ const onSendMessage = (data = {}) => {
ServePublishMessage(message) ServePublishMessage(message)
.then(({ code, message }) => { .then(({ code, message }) => {
if (code == 200) { if (code == 200) {
// callBack(true) if (callBack) {
callBack(true)
}
} else { } else {
message.warning(message) message.warning(message)
} }
@ -466,7 +520,7 @@ const onSendTextEvent = lodash.throttle((value) => {
mentions: data.mentionUids, mentions: data.mentionUids,
} }
onSendMessage(message) onSendMessage(message, callBack)
}, 1000) }, 1000)
// //
@ -717,6 +771,12 @@ const multipleChoose = (item) => {
const actionCite = (item) => { const actionCite = (item) => {
console.log('引用') console.log('引用')
state.value.quoteInfo = item
}
//
const clearQuoteInfo = () => {
state.value.quoteInfo = null
} }
const actionWithdraw = (item) => { const actionWithdraw = (item) => {
@ -804,6 +864,35 @@ watch(
}, },
) )
watch(
() => virtualList.value,
(newValue, oldValue) => {
if (newValue) {
const dialogueList = getDialogueList(talkParams.index_name)
// console.log(newValue[newValue.length - 1]?.sequence, dialogueList?.records?.[0]?.sequence)
if (
newValue[newValue.length - 1]?.sequence ===
dialogueList.records?.[0]?.sequence
) {
//
state.value.localPageLoadDone = true
} else {
state.value.localPageLoadDone = false
}
}
},
{
deep: true,
},
)
const onScrollToLower = () => {
if (state.value.localPageLoadDone) {
//
onRefreshLoad()
}
}
const clearMultiSelect = () => { const clearMultiSelect = () => {
dialogueStore.setMultiSelect(false) dialogueStore.setMultiSelect(false)
virtualList.value.forEach((item) => { virtualList.value.forEach((item) => {
@ -823,7 +912,7 @@ const initData = async () => {
no_limit: dialogueList ? 1 : 0, no_limit: dialogueList ? 1 : 0,
} }
await onLoad({ ...objT }) await onLoad({ ...objT })
zpagingRef.value?.complete(records.value) zpagingRef.value?.setLocalPaging(records.value)
} }
// //
@ -833,7 +922,7 @@ const toChatSettingsPage = () => {
'/pages/chatSettings/index?groupId=' + '/pages/chatSettings/index?groupId=' +
talkParams?.receiver_id + talkParams?.receiver_id +
'&sessionId=' + '&sessionId=' +
state.sessionId, state.value.sessionId,
}) })
} }
@ -887,6 +976,27 @@ onUnmounted(() => {
.footBox { .footBox {
min-height: 162rpx; min-height: 162rpx;
background-color: #fff; background-color: #fff;
.quote-area {
margin: 4rpx 0 0 0;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
overflow: hidden;
width: 100%;
span {
display: -webkit-inline-box;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
width: 100%;
}
img {
margin: 0 0 0 30rpx;
flex-shrink: 0;
}
}
} }
.load-toolbar { .load-toolbar {
@ -1033,6 +1143,7 @@ onUnmounted(() => {
text-overflow: ellipsis; text-overflow: ellipsis;
-webkit-line-clamp: 3; -webkit-line-clamp: 3;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
overflow: hidden;
} }
} }
@ -1089,6 +1200,10 @@ onUnmounted(() => {
} }
.quillBox { .quillBox {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
:deep(.ql-clipboard) { :deep(.ql-clipboard) {
position: relative; position: relative;
opacity: 0; opacity: 0;

View File

@ -47,7 +47,7 @@
</div> </div>
</div> </div>
<div <div style="flex-shrink: 0;"
class="text-[#000000] text-[28rpx] font-medium opacity-26 ml-[24rpx] time_right" class="text-[#000000] text-[28rpx] font-medium opacity-26 ml-[24rpx] time_right"
> >
{{ beautifyTime(props.data.updated_at) }} {{ beautifyTime(props.data.updated_at) }}

View File

@ -1,116 +1,110 @@
<template> <template>
<div class="outer-layer"> <div class="outer-layer">
<ZPaging <ZPaging
class="paging_container" class="paging_container"
ref="paging" ref="paging"
refresher-enabled refresher-enabled
:refresher-threshold="80" :refresher-threshold="80"
:refresher-max-angle="0" :refresher-max-angle="0"
:refresher-pull-rate="0.5" :refresher-pull-rate="0.5"
:refresher-fixed-bac-height="80" :refresher-fixed-bac-height="80"
refresher-fixed-background="#F9F9FD" refresher-fixed-background="#F9F9FD"
refresher-background="#F9F9FD" refresher-background="#F9F9FD"
v-model="items" v-model="items"
@query="queryList" @query="queryList"
:loading-more-enabled="false" :loading-more-enabled="false"
:refresher-end-bounce-enabled="false" :refresher-end-bounce-enabled="false"
:auto="true" :auto="true"
:empty-view-show="isEmptyViewShow" :empty-view-show="isEmptyViewShow"
@refresherRefresh="onRefresh" @refresherRefresh="onRefresh"
> :show-scrollbar="false"
<template #top> >
<div> <template #top>
<tm-navbar <div>
class="index_top_navbar" <tm-navbar
:hideBack="false" class="index_top_navbar"
hideHome :hideBack="false"
title="" hideHome
:leftWidth="420" title=""
> :leftWidth="420"
<template v-slot:left> :showStatusBar="false"
<div class="flex items-center ml-[48rpx]"> >
<image <template v-slot:left>
class="w-[72rpx] h-[72rpx]" <div class="flex items-center ml-[48rpx]">
style="border-radius: 50%" <image
:src="userStore.avatar" class="w-[72rpx] h-[72rpx]"
mode="scaleToFill" style="border-radius: 50%;"
/> :src="userStore.avatar"
<div class="ml-[24rpx] text-[36rpx] font-bold"> mode="scaleToFill"
{{ userStore.nickname }} />
</div> <div class="ml-[24rpx] text-[36rpx] font-bold">
</div> {{ userStore.nickname }}
</template> </div>
<template v-slot:right> </div>
<div class="mr-[48rpx] popoverBox"> </template>
<tm-popover position="br" color="#333333" :width="260"> <template v-slot:right>
<image <div class="mr-[48rpx] popoverBox">
class="w-[48rpx] h-[48rpx]" <tm-popover position="br" color="#333333" :width="260">
style="border-radius: 50%" <image
:src="addCircle" class="w-[48rpx] h-[48rpx]"
mode="scaleToFill" style="border-radius: 50%;"
/> :src="addCircle"
<template v-slot:label> mode="scaleToFill"
<div />
class="w-full h-[208rpx] pt-[22rpx] pb-[32rpx] pl-[14rpx] pr-[12rpx]" <template v-slot:label>
> <div
<div class="w-full h-[208rpx] pt-[22rpx] pb-[32rpx] pl-[14rpx] pr-[12rpx]"
@click="creatGroupChat" >
class="flex items-center pl-[22rpx] mb-[32rpx]" <div
> @click="creatGroupChat"
<div class="mr-[26rpx] flex items-center"> class="flex items-center pl-[22rpx] mb-[32rpx]"
<tm-image >
:width="40" <div class="mr-[26rpx] flex items-center">
:height="39" <tm-image
:src="cahtPopover" :width="40"
></tm-image> :height="39"
</div> :src="cahtPopover"
<div ></tm-image>
class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold" </div>
> <div
发起群聊 class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold"
</div> >
</div> 发起群聊
<div class="divider"></div> </div>
<div </div>
@click="toAddressBookPage" <div class="divider"></div>
class="flex items-center pl-[22rpx] mt-[32rpx]" <div
> @click="toAddressBookPage"
<div class="mr-[26rpx] flex items-center"> class="flex items-center pl-[22rpx] mt-[32rpx]"
<tm-image >
:width="40" <div class="mr-[26rpx] flex items-center">
:height="43" <tm-image
:src="zu3289" :width="40"
></tm-image> :height="43"
</div> :src="zu3289"
<div ></tm-image>
class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold" </div>
> <div
通讯录 class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold"
</div> >
</div> 通讯录
</div> </div>
</template> </div>
</tm-popover> </div>
</div> </template>
</template> </tm-popover>
</tm-navbar> </div>
</div> </template>
</template> </tm-navbar>
<template #refresher="{ refresherStatus }"> </div>
<custom-refresher :status="refresherStatus" /> </template>
</template> <template #refresher="{ refresherStatus }">
<custom-refresher :status="refresherStatus" />
</template>
<div class="content"> <div class="content">
<div class="root"> <div class="root">
<div class="searchRoot" @click="toSearchPage"> <div class="searchRoot" @click="toSearchPage">
<tm-input <customInput :disabled="true"></customInput>
placeholder="请输入…"
color="#F9F9FD"
:round="1"
prefix="tmicon-search"
prefixColor="#46299D"
placeholderStyle="color:#BABABA"
class="input_search"
></tm-input>
</div> </div>
<div class="contentRoot"> <div class="contentRoot">
<chatItem <chatItem
@ -121,88 +115,95 @@
/> />
</div> </div>
</div> </div>
</div> </div>
</ZPaging> </ZPaging>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, watch, computed } from "vue"; import customInput from '@/components/custom-input/custom-input.vue'
import { onShow, onLoad } from "@dcloudio/uni-app"; import { ref, watch, computed } from 'vue'
import { useChatList } from "@/store/chatList/index.js"; import { onShow, onLoad } from '@dcloudio/uni-app'
import { useAuth } from "@/store/auth"; import { useChatList } from '@/store/chatList/index.js'
import { useTalkStore, useUserStore, useDialogueStore } from "@/store"; import { useAuth } from '@/store/auth'
import chatItem from "./components/chatItem.vue"; import { useTalkStore, useUserStore, useDialogueStore } from '@/store'
import addCircle from "@/static/image/chatList/addCircle.png"; import chatItem from './components/chatItem.vue'
import cahtPopover from "@/static/image/chatList/cahtPopover.png"; import addCircle from '@/static/image/chatList/addCircle.png'
import zu3289 from "@/static/image/chatList/zu3289@2x.png"; import cahtPopover from '@/static/image/chatList/cahtPopover.png'
import ZPaging from "@/uni_modules/z-paging/components/z-paging/z-paging.vue"; import zu3289 from '@/static/image/chatList/zu3289@2x.png'
const paging = ref(); import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
const isEmptyViewShow = ref(false); const paging = ref()
const talkStore = useTalkStore(); const isEmptyViewShow = ref(false)
const userStore = useUserStore(); const talkStore = useTalkStore()
const dialogueStore = useDialogueStore(); const userStore = useUserStore()
const { userInfo } = useAuth(); const dialogueStore = useDialogueStore()
const { userInfo } = useAuth()
const topItems = computed(() => talkStore.topItems); const topItems = computed(() => talkStore.topItems)
const items = computed(() => { const items = computed(() => {
// if (searchKeyword.value.length === 0) { // if (searchKeyword.value.length === 0) {
console.log(talkStore.talkItems); console.log(talkStore.talkItems)
return talkStore.talkItems; return talkStore.talkItems
// } // }
// return talkStore.talkItems.filter((item) => { // return talkStore.talkItems.filter((item) => {
// let keyword = item.remark || item.name // let keyword = item.remark || item.name
// return keyword.toLowerCase().indexOf(searchKeyword.value.toLowerCase()) != -1 // return keyword.toLowerCase().indexOf(searchKeyword.value.toLowerCase()) != -1
// }) // })
}); })
const queryList = (pageNo, pageSize) => { const queryList = (pageNo, pageSize) => {
// paging.value.complete(res.data.list); // paging.value.complete(res.data.list);
console.log(talkStore); console.log(talkStore)
talkStore.loadTalkList().then(() => { talkStore
isEmptyViewShow.value = talkStore.talkItems.length === 0; .loadTalkList()
// .then(() => {
paging.value.complete(talkStore.talkItems); isEmptyViewShow.value = talkStore.talkItems.length === 0
}).catch(error => { //
// paging.value.complete(talkStore.talkItems)
console.error('加载失败', error); })
paging.value.complete(false); .catch((error) => {
}); //
}; console.error('加载失败', error)
paging.value.complete(false)
})
}
// //
const onRefresh = () => { const onRefresh = () => {
console.log('触发下拉刷新'); console.log('触发下拉刷新')
talkStore.loadTalkList().then(() => { talkStore
// .loadTalkList()
paging.value.complete(talkStore.talkItems); .then(() => {
}).catch(error => { //
// paging.value.complete(talkStore.talkItems)
console.error('加载失败', error); })
paging.value.complete(false); .catch((error) => {
}); //
}; console.error('加载失败', error)
paging.value.complete(false)
})
}
const creatGroupChat = () => { const creatGroupChat = () => {
uni.navigateTo({ uni.navigateTo({
url: "/pages/creatGroupChat/index", url: '/pages/creatGroupChat/index',
}); })
}; }
const toSearchPage = () => { const toSearchPage = () => {
uni.navigateTo({ uni.navigateTo({
url: "/pages/search/index", url: '/pages/search/index',
}); })
}; }
// //
const toAddressBookPage = () => { const toAddressBookPage = () => {
uni.navigateTo({ uni.navigateTo({
url: "/pages/chooseByDeps/index?chooseMode=3", url: '/pages/chooseByDeps/index?chooseMode=3',
}); })
}; }
/* watch( /* watch(
() => talkStore, () => talkStore,
@ -214,106 +215,108 @@ const toAddressBookPage = () => {
onShow(() => { onShow(() => {
// //
talkStore.loadTalkList().then(() => { talkStore
// paging .loadTalkList()
if (paging.value) { .then(() => {
paging.value.reload(); // paging
} if (paging.value) {
}).catch(error => { paging.value.reload()
console.error('页面显示时数据加载失败', error); }
}); })
}); .catch((error) => {
console.error('页面显示时数据加载失败', error)
})
})
onLoad((options) => { onLoad((options) => {
console.log(options); console.log(options)
if (options?.openSessionIndexName) { if (options?.openSessionIndexName) {
if (items?.value?.length > 0) { if (items?.value?.length > 0) {
items.value.forEach((openSession) => { items.value.forEach((openSession) => {
if (openSession.index_name === options?.openSessionIndexName) { if (openSession.index_name === options?.openSessionIndexName) {
dialogueStore.setDialogue(openSession); dialogueStore.setDialogue(openSession)
if (openSession.unread_num > 0) { if (openSession.unread_num > 0) {
ServeClearTalkUnreadNum({ ServeClearTalkUnreadNum({
talk_type: openSession.talk_type, talk_type: openSession.talk_type,
receiver_id: openSession.receiver_id, receiver_id: openSession.receiver_id,
}).then(() => { }).then(() => {
talkStore.updateItem({ talkStore.updateItem({
index_name: openSession.index_name, index_name: openSession.index_name,
unread_num: 0, unread_num: 0,
}); })
}); })
} }
uni.navigateTo({ uni.navigateTo({
url: "/pages/dialog/index?sessionId=" + openSession.id, url: '/pages/dialog/index?sessionId=' + openSession.id,
}); })
} }
}); })
} }
} }
}); })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
uni-page-body, uni-page-body,
page { page {
height: 100vh; height: 100vh;
background-image: url("@/static/image/clockIn/z3280@3x.png"); background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover; background-size: cover;
flex-direction: column; flex-direction: column;
} }
.outer-layer { .outer-layer {
height: 100%; height: 100%;
} }
.paging_container{ .paging_container {
// border: 1px solid red; // border: 1px solid red;
background: #fff; background: #fff;
margin: 0 32rpx 20rpx 32rpx; margin: 0 32rpx 20rpx 32rpx;
} }
::v-deep .index_top_navbar > .statusHeight:first-child { ::v-deep .index_top_navbar > .statusHeight:first-child {
height: 70px !important; height: 70px !important;
} }
::v-deep .index_top_navbar .statusHeightTop { ::v-deep .index_top_navbar .statusHeightTop {
height: 70px !important; height: 70px !important;
} }
::v-deep .index_top_navbar .statusHeightTop > .noNvueBorder:first-child { ::v-deep .index_top_navbar .statusHeightTop > .noNvueBorder:first-child {
height: 70px !important; height: 70px !important;
} }
.content{ .content {
// overflow: auto;
// overflow: auto;
} }
.root { .root {
flex: 1; flex: 1;
padding: 20rpx 0; padding: 20rpx 0;
} }
.searchRoot { .searchRoot {
background-color: #fff; background-color: #fff;
padding: 22rpx 18rpx; padding: 22rpx 18rpx;
border-bottom: 16rpx rgba(249, 249, 253, 1) solid; border-bottom: 16rpx rgba(249, 249, 253, 1) solid;
::v-deep .noNvueBorder > .noNvueBorder > .noNvueBorder { ::v-deep .noNvueBorder > .noNvueBorder > .noNvueBorder {
background: #f9f9fd !important; background: #f9f9fd !important;
} }
} }
.contentRoot { .contentRoot {
margin-top: 20rpx; margin-top: 20rpx;
background-color: #fff; background-color: #fff;
// min-height: calc(100vh - 180px); // min-height: calc(100vh - 180px);
} }
.divider { .divider {
height: 1rpx; height: 1rpx;
background-color: #7c7c7c; background-color: #7c7c7c;
opacity: 0.6; opacity: 0.6;
} }
.popoverBox { .popoverBox {
:deep(.popover-bcc) { :deep(.popover-bcc) {
transform: translateX(16rpx) translateY(48rpx); transform: translateX(16rpx) translateY(48rpx);
} }
} }
</style> </style>

View File

@ -446,7 +446,11 @@ const inputSearchText = (e) => {
} }
// //
const cancelSearch = () => {} const cancelSearch = () => {
uni.navigateBack({
delta: 1,
})
}
// //
const queryAllSearch = () => { const queryAllSearch = () => {

View File

@ -99,9 +99,8 @@ export const useDialogueListStore = createGlobalState(() => {
zpagingRef.value = params zpagingRef.value = params
} }
const zpagingComplete = (index_name) => { const zpagingComplete = (index_name, newRecords) => {
const item = getDialogueList(index_name) zpagingRef.value?.complete(lodash.cloneDeep(newRecords).reverse())
zpagingRef.value?.complete(lodash.cloneDeep(item.records).reverse())
} }
const addChatRecord = (indexName, item) => { const addChatRecord = (indexName, item) => {

View File

@ -1,78 +1,98 @@
<template> <template>
<view> <view>
<view v-if="props.isPlace" class="statusHeight" :style="{ height: _barHeight + 'px' }"></view> <view
<view class="fixed l-0 t-0 statusHeightTop flex" :style="{ width: _width + 'px', height: _barHeight + 'px' }"> v-if="props.isPlace"
<tm-sheet class="statusHeight"
@click="emits('click', $event)" :style="{ height: _barHeight + 'px' }"
:blur="_blur" ></view>
:color="props.color" <view
:_class="_class" class="fixed l-0 t-0 statusHeightTop flex"
:_style="_style" :style="{ width: _width + 'px', height: _barHeight + 'px' }"
:followTheme="props.followTheme" >
:follow-dark="props.followDark" <tm-sheet
:dark="props.dark" @click="emits('click', $event)"
:round="props.round" :blur="_blur"
:shadow="props.shadow" :color="props.color"
:outlined="props.outlined" :_class="_class"
:border="props.border" :_style="_style"
:borderStyle="props.borderStyle" :followTheme="props.followTheme"
:borderDirection="props.borderDirection" :follow-dark="props.followDark"
:text="props.text" :dark="props.dark"
:transprent="props.transprent" :round="props.round"
:linear="props.linear" :shadow="props.shadow"
:linearDeep="props.linearDeep" :outlined="props.outlined"
:margin="props.margin" :border="props.border"
:padding="props.padding" :borderStyle="props.borderStyle"
:height="_barHeight" :borderDirection="props.borderDirection"
:width="_width" :text="props.text"
unit="px" :transprent="props.transprent"
:darkBgColor="props.darkBgColor" :linear="props.linear"
> :linearDeep="props.linearDeep"
<view class="statusHeight" :style="{ height: statusBarHeight + 'px' }"></view> :margin="props.margin"
:padding="props.padding"
:height="_barHeight"
:width="_width"
unit="px"
:darkBgColor="props.darkBgColor"
>
<view
class="statusHeight"
:style="{ height: statusBarHeight + 'px' }"
v-if="props?.showStatusBar"
></view>
<view class="flex flex-row flex-1 flex-row flex-row-center-between"> <view class="flex flex-row flex-1 flex-row flex-row-center-between">
<view class="flex-row flex flex-row-center-start" :style="{ width: _leftWidth + 'rpx' }"> <view
<!-- #ifndef MP-ALIPAY --> class="flex-row flex flex-row-center-start"
<tm-icon :style="{ width: _leftWidth + 'rpx' }"
:unit="props.unit" >
:font-size="props.iconFontSize" <!-- #ifndef MP-ALIPAY -->
_class="pointer pb-12 pt-12 px-24" <tm-icon
:color="_homeColor" :unit="props.unit"
@click="goback" :font-size="props.iconFontSize"
v-if="_pages > 1 && !props.hideBack" _class="pointer pb-12 pt-12 px-24"
name="tmicon-angle-left" :color="_homeColor"
></tm-icon> @click="goback"
<tm-icon v-if="_pages > 1 && !props.hideBack"
:unit="props.unit" name="tmicon-angle-left"
_class="pointer pb-12 pt-12 px-24" ></tm-icon>
@click="backhome" <tm-icon
v-if="_pages == 1 && !hideHome" :unit="props.unit"
:color="_homeColor" _class="pointer pb-12 pt-12 px-24"
:font-size="props.iconFontSize" @click="backhome"
name="tmicon-md-home" v-if="_pages == 1 && !hideHome"
></tm-icon> :color="_homeColor"
<!-- #endif --> :font-size="props.iconFontSize"
<slot name="left"></slot> name="tmicon-md-home"
</view> ></tm-icon>
<view class="flex flex-row-center-center flex-col" :style="{ width: contentwidth + 'px' }"> <!-- #endif -->
<slot> <slot name="left"></slot>
<tm-text </view>
:unit="props.unit" <view
_class="text-weight-b text-overflow-1" class="flex flex-row-center-center flex-col"
:color="_fontColor" :style="{ width: contentwidth + 'px' }"
:font-size="props.fontSize" >
:label="_title" <slot>
></tm-text> <tm-text
:unit="props.unit"
_class="text-weight-b text-overflow-1"
:color="_fontColor"
:font-size="props.fontSize"
:label="_title"
></tm-text>
<slot name="subTitle"></slot> <slot name="subTitle"></slot>
</slot> </slot>
</view> </view>
<view class="flex-row flex flex-row-center-end" :style="{ width: _rightWidth + 'rpx' }"> <view
<slot name="right"></slot> class="flex-row flex flex-row-center-end"
</view> :style="{ width: _rightWidth + 'rpx' }"
</view> >
</tm-sheet> <slot name="right"></slot>
</view> </view>
</view> </view>
</tm-sheet>
</view>
</view>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
/** /**
@ -83,148 +103,167 @@ import tmSheet from '../tm-sheet/tm-sheet.vue'
import tmText from '../tm-text/tm-text.vue' import tmText from '../tm-text/tm-text.vue'
import tmIcon from '../tm-icon/tm-icon.vue' import tmIcon from '../tm-icon/tm-icon.vue'
import { custom_props } from '../../tool/lib/minxs' import { custom_props } from '../../tool/lib/minxs'
import { getCurrentInstance, computed, ref, provide, inject, onUpdated, onMounted, onUnmounted, nextTick, watch, PropType } from 'vue' import {
getCurrentInstance,
computed,
ref,
provide,
inject,
onUpdated,
onMounted,
onUnmounted,
nextTick,
watch,
PropType,
} from 'vue'
import { useTmpiniaStore } from '../../tool/lib/tmpinia' import { useTmpiniaStore } from '../../tool/lib/tmpinia'
const store = useTmpiniaStore() const store = useTmpiniaStore()
const emits = defineEmits(['click', 'close']) const emits = defineEmits(['click', 'close'])
const proxy = getCurrentInstance()?.proxy ?? null const proxy = getCurrentInstance()?.proxy ?? null
const props = defineProps({ const props = defineProps({
...custom_props, ...custom_props,
followTheme: { followTheme: {
type: [Boolean, String], type: [Boolean, String],
default: true default: true,
}, },
transprent: { transprent: {
type: [Boolean, String], type: [Boolean, String],
default: false default: false,
}, },
color: { color: {
type: [String], type: [String],
default: 'white' default: 'white',
}, },
text: { text: {
type: [Boolean], type: [Boolean],
default: false default: false,
}, },
border: { border: {
type: [Number], type: [Number],
default: 0 default: 0,
}, },
shadow: { shadow: {
type: [Number], type: [Number],
default: 1 default: 1,
}, },
borderDirection: { borderDirection: {
type: String as PropType< type: String as PropType<
| 'all' | 'all'
| 'bottom' | 'bottom'
| 'bottomleft' | 'bottomleft'
| 'bottomright' | 'bottomright'
| 'left' | 'left'
| 'leftright' | 'leftright'
| 'right' | 'right'
| 'top' | 'top'
| 'topbottom' | 'topbottom'
| 'topleft' | 'topleft'
| 'topright' | 'topright'
| 'x' | 'x'
| 'y' | 'y'
>, >,
default: 'bottom' default: 'bottom',
}, },
round: { round: {
type: [Number], type: [Number],
default: 0 default: 0,
}, },
margin: { margin: {
type: Array as PropType<Array<number>>, type: Array as PropType<Array<number>>,
default: () => [0, 0] default: () => [0, 0],
}, },
padding: { padding: {
type: Array as PropType<Array<number>>, type: Array as PropType<Array<number>>,
default: () => [0, 0] default: () => [0, 0],
}, },
height: { height: {
type: [Number], type: [Number],
default: 44 default: 44,
}, },
// //
leftWidth: { leftWidth: {
type: [Number], type: [Number],
default: 220 default: 220,
}, },
// //
rightWidth: { rightWidth: {
type: [Number], type: [Number],
default: 220 default: 220,
}, },
fontSize: { fontSize: {
type: [Number], type: [Number],
default: 30 default: 30,
}, },
iconFontSize: { iconFontSize: {
type: [Number], type: [Number],
default: 37 default: 37,
}, },
title: { title: {
type: [String], type: [String],
default: '标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题' default: '标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题标题',
}, },
//使 //使
fontColor: { fontColor: {
type: [String], type: [String],
default: '' default: '',
}, },
homeColor: { homeColor: {
type: [String], type: [String],
default: '' default: '',
}, },
hideHome: { hideHome: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
hideBack: { hideBack: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
///pages/index/index ///pages/index/index
homePath: { homePath: {
type: [String], type: [String],
default: '/pages/index/index' default: '/pages/index/index',
}, },
beforeBack: { beforeBack: {
type: [Boolean, Function], type: [Boolean, Function],
default: () => true default: () => true,
}, },
blur: { blur: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
unit: { unit: {
type: String, type: String,
default: 'rpx' default: 'rpx',
}, },
// //
//使使 //使使
// //
darkBgColor: { darkBgColor: {
type: String, type: String,
default: '' default: '',
}, },
/**是否占位,如果为false,底部内容会被导航遮盖,true则会店内内容位置. */ /**是否占位,如果为false,底部内容会被导航遮盖,true则会店内内容位置. */
isPlace: { isPlace: {
type: Boolean, type: Boolean,
default: true default: true,
} },
//
showStatusBar: {
type: Boolean,
default: true,
},
}) })
const _height = computed(() => props.height) const _height = computed(() => props.height)
const _width = uni.getSystemInfoSync().windowWidth const _width = uni.getSystemInfoSync().windowWidth
const statusBarHeight=window?.plus?.navigator?.getStatusbarHeight() ?? 0 const statusBarHeight = window?.plus?.navigator?.getStatusbarHeight() ?? 0
const _barHeight = computed(() => statusBarHeight + _height.value) const _barHeight = computed(() =>
props?.showStatusBar ? statusBarHeight + _height.value : _height.value,
)
const _leftWidth = computed(() => props.leftWidth) const _leftWidth = computed(() => props.leftWidth)
const _rightWidth = computed(() => props.rightWidth) const _rightWidth = computed(() => props.rightWidth)
const contentwidth = computed(() => { const contentwidth = computed(() => {
return _width - uni.upx2px(_leftWidth.value) - uni.upx2px(_rightWidth.value) return _width - uni.upx2px(_leftWidth.value) - uni.upx2px(_rightWidth.value)
}) })
const _title = computed(() => props.title) const _title = computed(() => props.title)
const _fontColor = computed(() => props.fontColor) const _fontColor = computed(() => props.fontColor)
@ -232,53 +271,53 @@ const _homeColor = computed(() => props.homeColor)
const _blur = computed(() => props.blur) const _blur = computed(() => props.blur)
const _pages = ref(0) const _pages = ref(0)
onMounted(() => { onMounted(() => {
_pages.value = getCurrentPages().length _pages.value = getCurrentPages().length
}) })
const backhome = () => { const backhome = () => {
uni.reLaunch({ uni.reLaunch({
url: props.homePath url: props.homePath,
}) })
} }
let timerId = NaN let timerId = NaN
function debounce(func: Function, wait = 500, immediate = false) { function debounce(func: Function, wait = 500, immediate = false) {
// //
if (!isNaN(timerId)) clearTimeout(timerId) if (!isNaN(timerId)) clearTimeout(timerId)
// //
if (immediate) { if (immediate) {
var callNow = !timerId var callNow = !timerId
timerId = setTimeout(() => { timerId = setTimeout(() => {
timerId = NaN timerId = NaN
}, wait) }, wait)
if (callNow) typeof func === 'function' && func() if (callNow) typeof func === 'function' && func()
} else { } else {
// timeoutwaitfunc // timeoutwaitfunc
timerId = setTimeout(() => { timerId = setTimeout(() => {
typeof func === 'function' && func() typeof func === 'function' && func()
}, wait) }, wait)
} }
} }
const goback = () => { const goback = () => {
debounce( debounce(
async () => { async () => {
if (typeof props.beforeBack === 'function') { if (typeof props.beforeBack === 'function') {
let p = await props.beforeBack() let p = await props.beforeBack()
if (typeof p === 'function') { if (typeof p === 'function') {
p = await p() p = await p()
} }
if (!p) return if (!p) return
} }
uni.navigateBack({}) uni.navigateBack({})
}, },
250, 250,
true true,
) )
} }
</script> </script>
<style scoped> <style scoped>
.statusHeightTop { .statusHeightTop {
z-index: 400; z-index: 400;
} }
</style> </style>

View File

@ -149,5 +149,6 @@
"button.multiple.choice": "多选", "button.multiple.choice": "多选",
"button.text.close": "关闭", "button.text.close": "关闭",
"choose.deps.all": "全部", "choose.deps.all": "全部",
"choose.deps.current": "当前" "choose.deps.current": "当前",
"msg.type": "图片"
} }

View File

@ -1,43 +1,60 @@
import AutoImport from 'unplugin-auto-import/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import Components from 'unplugin-vue-components/vite'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni' import Uni from '@dcloudio/vite-plugin-uni'
import UniKuRoot from '@uni-ku/root' import UniKuRoot from '@uni-ku/root'
import { resolve } from "path" import { resolve } from 'node:path'
export default async () => { import UnoCSS from 'unocss/vite'
const UnoCSS = (await import('unocss/vite')).default
return defineConfig({ export default defineConfig({
envDir: './env', // 自定义env目录 envDir: './env', // 自定义env目录
resolve: { resolve: {
alias: [ alias: [
{ {
find: "@", find: "@",
replacement: resolve(__dirname, 'src') replacement: resolve(process.cwd(), 'src')
}
]
},
server: {
host: '0.0.0.0', // 监听所有网络接口
port: 2367,
// 选项写法
proxy: {
'/pag': {
target: 'https://cdn.tmui.design',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '/api')
},
} }
}, ]
plugins: [ },
Uni(), server: {
UniKuRoot(), host: '0.0.0.0', // 监听所有网络接口
UnoCSS() port: 2367,
], // 选项写法
css: { proxy: {
preprocessorOptions: { '/pag': {
scss: { target: 'https://cdn.tmui.design',
additionalData: `@import "@/static/css/color.scss";`, changeOrigin: true,
}, rewrite: (path) => path.replace(/^\/api/, '/api')
}, },
} }
}) },
} plugins: [
Uni(),
UniKuRoot(),
UnoCSS(),
AutoImport({
imports: [
'vue',
{
'naive-ui': [
'useDialog',
'useMessage',
'useNotification',
'useLoadingBar'
]
}
]
}),
Components({
resolvers: [NaiveUiResolver()]
})
],
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/static/css/color.scss";`,
},
},
}
})