Merge branch 'wyfMain-dev'
Some checks failed
Check / lint (push) Has been cancelled
Check / typecheck (push) Has been cancelled
Check / build (build, 18.x, ubuntu-latest) (push) Has been cancelled
Check / build (build, 18.x, windows-latest) (push) Has been cancelled
Check / build (build:app, 18.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:app, 18.x, windows-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 18.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 18.x, windows-latest) (push) Has been cancelled

This commit is contained in:
wangyifeng 2025-01-08 16:35:54 +08:00
commit b93183eec3
54 changed files with 4797 additions and 871 deletions

View File

@ -39,7 +39,7 @@
"quill-mention": "^6.0.2", "quill-mention": "^6.0.2",
"vconsole": "^3.15.1", "vconsole": "^3.15.1",
"vue": "^3.3.8", "vue": "^3.3.8",
"vue-i18n": "^9.6.5" "vue-i18n": "11.0.0-rc.1"
}, },
"devDependencies": { "devDependencies": {
"@dcloudio/types": "^3.4.7", "@dcloudio/types": "^3.4.7",

View File

@ -93,8 +93,8 @@ importers:
specifier: ^3.3.8 specifier: ^3.3.8
version: 3.4.35(typescript@5.5.4) version: 3.4.35(typescript@5.5.4)
vue-i18n: vue-i18n:
specifier: ^9.6.5 specifier: 11.0.0-rc.1
version: 9.13.1(vue@3.4.35(typescript@5.5.4)) version: 11.0.0-rc.1(vue@3.4.35(typescript@5.5.4))
devDependencies: devDependencies:
'@dcloudio/types': '@dcloudio/types':
specifier: ^3.4.7 specifier: ^3.4.7
@ -1391,26 +1391,26 @@ packages:
'@iconify/utils@2.1.32': '@iconify/utils@2.1.32':
resolution: {integrity: sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==} resolution: {integrity: sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==}
'@intlify/core-base@11.0.0-rc.1':
resolution: {integrity: sha512-fnfZoa9pb1dKM3L1UkDUGLLrPFQ2BK98x4/fMqwS/fktUor34vQR/itPtfv652ZTplenXXLCEYjUYTGfDZgMTQ==}
engines: {node: '>= 16'}
'@intlify/core-base@9.1.9': '@intlify/core-base@9.1.9':
resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==} resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
'@intlify/core-base@9.13.1':
resolution: {integrity: sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==}
engines: {node: '>= 16'}
'@intlify/devtools-if@9.1.9': '@intlify/devtools-if@9.1.9':
resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==} resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
'@intlify/message-compiler@11.0.0-rc.1':
resolution: {integrity: sha512-TGw2uBfuTFTegZf/BHtUQBEKxl7Q/dVGLoqRIdw8lFsp9g/53sYn5iD+0HxIzdYjbWL6BTJMXCPUHp9PxDTRPw==}
engines: {node: '>= 16'}
'@intlify/message-compiler@9.1.9': '@intlify/message-compiler@9.1.9':
resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==} resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
'@intlify/message-compiler@9.13.1':
resolution: {integrity: sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==}
engines: {node: '>= 16'}
'@intlify/message-resolver@9.1.9': '@intlify/message-resolver@9.1.9':
resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==} resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@ -1419,14 +1419,14 @@ packages:
resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==} resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
'@intlify/shared@11.0.0-rc.1':
resolution: {integrity: sha512-8tR1xe7ZEbkabTuE/tNhzpolygUn9OaYp9yuYAF4MgDNZg06C3Qny80bes2/e9/Wm3aVkPUlCw6WgU7mQd0yEg==}
engines: {node: '>= 16'}
'@intlify/shared@9.1.9': '@intlify/shared@9.1.9':
resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==} resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
'@intlify/shared@9.13.1':
resolution: {integrity: sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==}
engines: {node: '>= 16'}
'@intlify/vue-devtools@9.1.9': '@intlify/vue-devtools@9.1.9':
resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==} resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@ -4819,8 +4819,8 @@ packages:
'@vue/composition-api': '@vue/composition-api':
optional: true optional: true
vue-i18n@9.13.1: vue-i18n@11.0.0-rc.1:
resolution: {integrity: sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==} resolution: {integrity: sha512-qbdCbA537HEdr2yXQ4ec/OMDsoHjod1DwnWbrf+l4Cu/O7CYTCKsOyITUm3RmrCJgRnoVycuR6i/JWdNTJvD5g==}
engines: {node: '>= 16'} engines: {node: '>= 16'}
peerDependencies: peerDependencies:
vue: ^3.0.0 vue: ^3.0.0
@ -6730,6 +6730,11 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@intlify/core-base@11.0.0-rc.1':
dependencies:
'@intlify/message-compiler': 11.0.0-rc.1
'@intlify/shared': 11.0.0-rc.1
'@intlify/core-base@9.1.9': '@intlify/core-base@9.1.9':
dependencies: dependencies:
'@intlify/devtools-if': 9.1.9 '@intlify/devtools-if': 9.1.9
@ -6739,26 +6744,21 @@ snapshots:
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.9
'@intlify/vue-devtools': 9.1.9 '@intlify/vue-devtools': 9.1.9
'@intlify/core-base@9.13.1':
dependencies:
'@intlify/message-compiler': 9.13.1
'@intlify/shared': 9.13.1
'@intlify/devtools-if@9.1.9': '@intlify/devtools-if@9.1.9':
dependencies: dependencies:
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.9
'@intlify/message-compiler@11.0.0-rc.1':
dependencies:
'@intlify/shared': 11.0.0-rc.1
source-map-js: 1.2.0
'@intlify/message-compiler@9.1.9': '@intlify/message-compiler@9.1.9':
dependencies: dependencies:
'@intlify/message-resolver': 9.1.9 '@intlify/message-resolver': 9.1.9
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.9
source-map: 0.6.1 source-map: 0.6.1
'@intlify/message-compiler@9.13.1':
dependencies:
'@intlify/shared': 9.13.1
source-map-js: 1.2.0
'@intlify/message-resolver@9.1.9': {} '@intlify/message-resolver@9.1.9': {}
'@intlify/runtime@9.1.9': '@intlify/runtime@9.1.9':
@ -6767,9 +6767,9 @@ snapshots:
'@intlify/message-resolver': 9.1.9 '@intlify/message-resolver': 9.1.9
'@intlify/shared': 9.1.9 '@intlify/shared': 9.1.9
'@intlify/shared@9.1.9': {} '@intlify/shared@11.0.0-rc.1': {}
'@intlify/shared@9.13.1': {} '@intlify/shared@9.1.9': {}
'@intlify/vue-devtools@9.1.9': '@intlify/vue-devtools@9.1.9':
dependencies: dependencies:
@ -10890,10 +10890,10 @@ snapshots:
dependencies: dependencies:
vue: 3.4.35(typescript@5.5.4) vue: 3.4.35(typescript@5.5.4)
vue-i18n@9.13.1(vue@3.4.35(typescript@5.5.4)): vue-i18n@11.0.0-rc.1(vue@3.4.35(typescript@5.5.4)):
dependencies: dependencies:
'@intlify/core-base': 9.13.1 '@intlify/core-base': 11.0.0-rc.1
'@intlify/shared': 9.13.1 '@intlify/shared': 11.0.0-rc.1
'@vue/devtools-api': 6.6.3 '@vue/devtools-api': 6.6.3
vue: 3.4.35(typescript@5.5.4) vue: 3.4.35(typescript@5.5.4)

View File

@ -18,6 +18,7 @@ init()
</script> </script>
<style lang="scss"> <style lang="scss">
@import "@/static/css/color.scss"; @import "@/static/css/color.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 */

View File

@ -207,6 +207,7 @@ export const ServeGroupAssignAdmin = (data) => {
}) })
} }
//指定人员禁言
export const ServeGroupNoSpeak = (data) => { export const ServeGroupNoSpeak = (data) => {
return request({ return request({
url: '/api/v1/group/no-speak', url: '/api/v1/group/no-speak',
@ -214,4 +215,3 @@ export const ServeGroupNoSpeak = (data) => {
data, data,
}) })
} }

47
src/api/search/index.js Normal file
View File

@ -0,0 +1,47 @@
import request from '@/service/index.js'
import qs from 'qs'
// ES搜索聊天记录-主页搜索什么都有
export const ServeSeachQueryAll = (data) => {
return request({
url: '/api/v1/elasticsearch/query-all',
method: 'POST',
data,
})
}
// ES搜索用户数据
export const ServeQueryUser = (data) => {
return request({
url: '/api/v1/elasticsearch/query-user',
method: 'POST',
data,
})
}
// ES搜索群组数据
export const ServeQueryGroup = (data) => {
return request({
url: '/api/v1/elasticsearch/query-group',
method: 'POST',
data,
})
}
// ES搜索聊天记录-指定用户、指定群、群与用户概览
export const ServeTalkRecord = (data) => {
return request({
url: '/api/v1/elasticsearch/query-talk-record',
method: 'POST',
data,
})
}
//查看存在聊天记录的天数
export const ServeTalkDate = (data) => {
return request({
url: '/api/v1/talk/date',
method: 'POST',
data,
})
}

View File

@ -0,0 +1,42 @@
<template>
<div class="custom-btn" :class="props.isBottom ? 'custom-btn-bottom' : ''">
<wd-button custom-class="custom-btn-class" @click="clickBtn">{{ props.btnText }}</wd-button>
</div>
</template>
<script setup>
import { reactive } from 'vue'
import { defineProps, defineEmits } from 'vue'
const state = reactive({})
const emits = defineEmits(['clickBtn'])
const props = defineProps({
isBottom: false, //
btnText: '', //
})
//
const clickBtn = () => {
emits('clickBtn')
}
</script>
<style scoped lang="scss">
.custom-btn {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.custom-btn-class {
background-color: $theme-primary;
padding: 18rpx 185rpx;
border-radius: 8rpx;
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
font-size: 28rpx;
font-weight: 500;
line-height: 40rpx;
}
}
.custom-btn-bottom {
width: 100%;
background-color: #fff;
padding: 14rpx 0 72rpx;
}
</style>

View File

@ -0,0 +1,58 @@
<template>
<div class="custom-search-input">
<tm-input
class="search-input"
placeholder="请输入…"
color="#F9F9FD"
:round="1"
prefix="tmicon-search"
prefixColor="#46299D"
:prefixLabel="props?.first_talk_record_infos?.receiver_name"
v-model.lazy="state.searchText"
@input="inputSearchText"
:showClear="true"
@clear="clearInput"
></tm-input>
</div>
</template>
<script setup>
import { defineProps, defineEmits, reactive, watch } from 'vue'
const props = defineProps({
searchText: String,
first_talk_record_infos: Object,
})
const state = reactive({
searchText: '', //
})
const emits = defineEmits(['inputSearchText'])
watch(
() => props.searchText,
(newSearchText) => {
state.searchText = newSearchText
},
)
//
const clearInput = () => {
inputSearchText('')
}
//
const inputSearchText = (e) => {
emits('inputSearchText', e)
}
</script>
<style scoped lang="scss">
.custom-search-input {
width: 100%;
.search-input {
width: 100%;
}
.search-input::v-deep .tmicon-times-circle-fill::before {
content: '\e82a';
color: #d2d2d5;
}
}
</style>

View File

@ -1,7 +1,7 @@
{ {
"easycom":{ "easycom": {
"autoscan": true, "autoscan": true,
"custom":{ "custom": {
"^tm-(.*)": "@/tmui/components/tm-$1/tm-$1.vue" "^tm-(.*)": "@/tmui/components/tm-$1/tm-$1.vue"
} }
}, },
@ -11,7 +11,7 @@
"type": "page", "type": "page",
"style": { "style": {
"navigationStyle": "custom", "navigationStyle": "custom",
"enablePullDownRefresh":false "enablePullDownRefresh": false
} }
}, },
{ {
@ -19,7 +19,7 @@
"type": "page", "type": "page",
"style": { "style": {
"navigationStyle": "custom", "navigationStyle": "custom",
"enablePullDownRefresh":false "enablePullDownRefresh": false
} }
}, },
{ {
@ -27,7 +27,7 @@
"type": "page", "type": "page",
"style": { "style": {
"navigationStyle": "custom", "navigationStyle": "custom",
"enablePullDownRefresh":false "enablePullDownRefresh": false
} }
}, },
{ {
@ -35,7 +35,7 @@
"type": "page", "type": "page",
"style": { "style": {
"navigationStyle": "custom", "navigationStyle": "custom",
"enablePullDownRefresh":false "enablePullDownRefresh": false
} }
}, },
{ {
@ -59,7 +59,7 @@
"type": "page", "type": "page",
"style": { "style": {
"navigationStyle": "custom", "navigationStyle": "custom",
"enablePullDownRefresh":false "enablePullDownRefresh": false
} }
}, },
{ {
@ -67,23 +67,116 @@
"type": "page", "type": "page",
"style": { "style": {
"navigationStyle": "custom", "navigationStyle": "custom",
"enablePullDownRefresh":false "enablePullDownRefresh": false
} }
}, },
{ {
"path": "pages/login/index", "path": "pages/login/index",
"type": "page", "type": "page",
"style": {} "style": {}
},
{
"path": "pages/search/index",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/search/moreResult/moreResult",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/search/moreResult/moreResultDetail",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/search/searchByCondition/index",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/chatSettings/index",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/chatSettings/groupManage/manageNotice",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/chatSettings/groupManage/editGroupName",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/chatSettings/groupManage/editAvatar",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/chatSettings/groupManage/manageGroupMembers",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/chatSettings/groupManage/manageGroupSilence",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/chatSettings/groupManage/selectMembers",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
"path": "pages/chatSettings/groupManage/manageGroupAdmin",
"type": "page",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
} }
], ],
"globalStyle": { "globalStyle": {
"backgroundColor": "#FFFFFF", "backgroundColor": "#FFFFFF",
"navigationBarBackgroundColor": "#FFFFFF", "navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTextstyle": "black", "navigationBarTextstyle": "black",
"navigationBarTitleText":"" "navigationBarTitleText": ""
}, },
"subPackages": [] "subPackages": []
} }

View File

@ -0,0 +1,117 @@
<template>
<div class="group-member-list">
<div
class="group-member-list-each"
v-for="(memberItem, memberIndex) in props?.memberList"
>
<div
class="group-member-each"
v-if="
props.memberListsLimit ? memberIndex < props.memberListsLimit : true
"
>
<div class="group-member-avatar">
<img v-if="memberItem.avatar" :src="memberItem.avatar" />
<span v-if="!memberItem.avatar" class="text-[24rpx] font-bold">
{{
memberItem.nickname.length >= 2
? memberItem.nickname.slice(-2)
: memberItem.nickname
}}
</span>
</div>
<div class="group-member-tag" v-if="memberIndex < 3">
<span class="text-[16rpx] font-regular">
{{ $t('group.identify.admin') }}
</span>
</div>
<div class="group-member-name">
<span class="text-[24rpx] font-regular">
{{ memberItem.nickname }}
</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
memberList: Array, //
memberListsLimit: Number, //
})
</script>
<style scoped lang="scss">
.group-member-list {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
.group-member-list-each {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: calc(100% / 5);
.group-member-each {
padding: 32rpx 0 0;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
.group-member-avatar {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
overflow: hidden;
background: linear-gradient(to right, #674bbc, #46299d);
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
}
span {
line-height: 34rpx;
color: #fff;
}
}
.group-member-tag {
position: absolute;
bottom: 34rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
background-color: #cf3050;
border-radius: 16rpx;
padding: 0 12rpx;
span {
line-height: 22rpx;
color: #fff;
}
}
.group-member-name {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 8rpx 0 0;
width: 100%;
span {
overflow: hidden;
text-overflow: ellipsis;
line-height: 34rpx;
color: #919191;
white-space: nowrap;
}
}
}
}
}
</style>

View File

@ -0,0 +1,142 @@
<template>
<div
class="select-member-item"
@click="clickItem(props?.memberItem)"
:class="
props.itemStyle === 'card'
? 'select-member-item-card'
: 'select-member-item-list'
"
>
<div class="member-info">
<slot name="left"></slot>
<div
class="select-member-item-avatar"
v-if="
props?.manageType === 'silence' ||
props?.manageType === 'searchRecord'
"
>
<img v-if="avatarImg !== 'textImg'" :src="avatarImg" />
<span v-if="avatarImg === 'textImg'" class="text-[24rpx] font-bold">
{{ imgText }}
</span>
</div>
<div
class="select-member-item-name"
v-if="
props?.manageType === 'silence' ||
props?.manageType === 'searchRecord'
"
>
<span class="text-[28rpx] font-medium">{{ nameText }}</span>
</div>
</div>
<div class="operate-btns">
<div class="btn-undo-silence">
<span v-if="props?.memberItem?.is_mute === 1">
{{ $t('chatSettings.btn.undoSilence') }}
</span>
<span v-if="props?.memberItem?.is_mute === 1">
{{ $t('silence.tag.hasDone') }}
</span>
</div>
</div>
</div>
</template>
<script setup>
import { computed, defineProps, defineEmits } from 'vue'
import { onMounted, reactive } from 'vue'
const emits = defineEmits(['clickItem'])
const props = defineProps({
memberItem: Object, //
manageType: String, //
itemStyle: String,
})
onMounted(() => {})
//
const avatarImg = computed(() => {
let avatar_img = props?.memberItem?.avatar
if (!avatar_img) {
avatar_img = 'textImg'
}
return avatar_img
})
//
const nameText = computed(() => {
let name = props?.memberItem?.nickname
return name
})
//
const imgText = computed(() => {
return nameText.value.length >= 2 ? nameText.value.slice(-2) : nameText.value
})
//item
const clickItem = () => {
emits('clickItem')
}
</script>
<style scoped lang="scss">
.select-member-item {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 18rpx 34rpx;
background-color: #fff;
width: 100%;
.member-info {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
.select-member-item-avatar {
background: linear-gradient(to right, #674bbc, #46299d);
width: 72rpx;
height: 72rpx;
border-radius: 50%;
overflow: hidden;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin: 0 20rpx 0 0;
img {
width: 100%;
height: 100%;
}
span {
color: #fff;
line-height: 34rpx;
}
}
.select-member-item-name {
span {
color: $theme-text;
line-height: 40rpx;
}
}
}
.operate-btns {
.btn-undo-silence {
span {
}
}
}
}
.select-member-item-card {
margin: 20rpx 0 0;
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
border-radius: 8rpx;
}
.select-member-item-list {
border-bottom: 1px solid $theme-border-color;
}
</style>

View File

@ -0,0 +1,118 @@
<template>
<div class="setting-form-item">
<div class="item-main">
<div class="item-main-label">
<span class="text-[32rpx] font-regular">{{ props?.item?.label }}</span>
</div>
<div class="item-main-value" @click="toManagePage(props?.item)">
<span class="text-[32rpx] font-regular" v-if="props?.item?.value">
{{ props?.item?.value }}
</span>
<img
v-if="props?.item?.hasPointer"
src="/src/static/image/chatSettings/pointer.png"
/>
<tm-switch
:width="88"
:height="48"
v-if="props?.item?.customInfo && props?.item?.customInfo === 'switch'"
barIcon=""
color="#46299D"
unCheckedColor="#EEEEEE"
:modelValue="modelValue"
@change="changeSwitch($event, props?.item)"
></tm-switch>
</div>
</div>
<div class="item-sub" v-if="props?.item?.subValue">
<span class="text-[32rpx] font-regular">{{ props?.item?.subValue }}</span>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits, computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const props = defineProps({
item: {
type: Object,
default() {
return {}
},
},
sessionInfo: {
type: Object,
default() {
return {}
},
},
})
const emits = defineEmits(['toManagePage', 'changeSwitch'])
const toManagePage = (item) => {
emits('toManagePage', item.label)
}
const modelValue = computed(() => {
let switchStatus = false
if (
props?.item?.label === t('chat.settings.topSession') &&
props?.sessionInfo?.is_top == 1
) {
switchStatus = true
}
if (
props?.item?.label === t('chat.settings.messageNoDisturb') &&
props?.sessionInfo?.is_disturb == 1
) {
switchStatus = true
}
return switchStatus
})
//
const changeSwitch = (e, item) => {
emits('changeSwitch', e, item.label)
}
</script>
<style lang="scss" scoped>
.setting-form-item {
.item-main {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
.item-main-label {
flex-shrink: 0;
span {
line-height: 44rpx;
color: $theme-text;
}
}
.item-main-value {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
span {
line-height: 44rpx;
color: #747474;
}
img {
width: 11rpx;
height: 18rpx;
margin: 0 0 0 32rpx;
}
}
}
.item-sub {
margin: 28rpx 0 0;
span {
line-height: 44rpx;
color: #747474;
}
}
}
</style>

View File

@ -0,0 +1,95 @@
<template>
<div class="outer-layer manage-group-info-page">
<div class="root">
<ZPaging ref="zPaging" :show-scrollbar="false">
<template #top>
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
<div class="navBar-title flex flex-col items-center justify-center">
<span class="text-[34rpx] font-medium">
{{ $t('chat.settings.editAvatar') }}
</span>
</div>
</tm-navbar>
</template>
<div class="edit-group-info">
<div class="group-avatar">
<img :src="state.groupAvatar" />
</div>
</div>
<customBtn :btnText="$t('button.text.edit')"></customBtn>
<template #bottom>
<div class="app-logo-icon">
<img src="/src/static/image/chatSettings/app-icon.png" />
</div>
</template>
</ZPaging>
</div>
</div>
</template>
<script setup>
import customBtn from '@/components/custom-btn/custom-btn.vue'
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import { onLoad } from '@dcloudio/uni-app'
import { reactive } from 'vue'
const state = reactive({
pageTitle: '', //
groupAvatar: '', //
groupName: '', //
})
onLoad((options) => {
console.log(options)
if (options.groupAvatar) {
state.groupAvatar = options.groupAvatar
}
})
//
const clearGroupNameInput = () => {
state.groupName = ''
}
//
const confirmEdit = () => {
console.log(state.groupName)
}
</script>
<style scoped lang="scss">
.outer-layer {
flex: 1;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-repeat: no-repeat;
}
.edit-group-info {
margin: 158rpx 94rpx 100rpx;
background-color: #fff;
box-shadow: 0 6px 12px 2px rgba(0, 0, 0, 0.16);
padding: 50rpx;
border-radius: 16rpx;
.group-avatar {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
img {
width: 460rpx;
height: 460rpx;
}
}
}
.app-logo-icon {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 0 0 44rpx;
img {
width: 72rpx;
height: 72rpx;
}
}
</style>

View File

@ -0,0 +1,127 @@
<template>
<div class="outer-layer manage-group-info-page">
<div class="root">
<ZPaging ref="zPaging" :show-scrollbar="false">
<template #top>
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
<div class="navBar-title flex flex-col items-center justify-center">
<span class="text-[34rpx] font-medium">
{{ $t('chat.settings.editGroupName') }}
</span>
</div>
</tm-navbar>
</template>
<div class="edit-group-info">
<div class="group-avatar">
<img :src="state.groupAvatar" />
</div>
<div class="group-name">
<span class="text-[28rpx] font-medium">
{{ $t('chat.settings.groupName') }}
</span>
<div class="groupNameInputArea">
<input
class="groupNameInput"
:placeholder="$t('edit.groupName.placeholder')"
placeholder-style="color:#B4B4B4;font-size:28rpx;font-weight:500;line-height:40rpx;"
v-model="state.groupName"
/>
<img
class="groupName-input-clearBtn"
src="/src/static/image/chatSettings/clear-btn.png"
@click="clearGroupNameInput"
/>
</div>
</div>
</div>
<template #bottom>
<customBtn
:isBottom="true"
:btnText="$t('ok')"
@clickBtn="confirmEdit"
></customBtn>
</template>
</ZPaging>
</div>
</div>
</template>
<script setup>
import customBtn from '@/components/custom-btn/custom-btn.vue'
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import { onLoad } from '@dcloudio/uni-app'
import { reactive } from 'vue'
const state = reactive({
pageTitle: '', //
groupAvatar: '', //
groupName: '', //
})
onLoad((options) => {
console.log(options)
if (options.groupAvatar) {
state.groupAvatar = options.groupAvatar
}
})
//
const clearGroupNameInput = () => {
state.groupName = ''
}
//
const confirmEdit = () => {
console.log(state.groupName)
}
</script>
<style scoped lang="scss">
.outer-layer {
flex: 1;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-repeat: no-repeat;
}
.edit-group-info {
.group-avatar {
padding: 250rpx 0 88rpx;
border-radius: 50%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
img {
width: 192rpx;
height: 192rpx;
}
}
.group-name {
padding: 0 32rpx;
span {
display: inline-block;
padding: 0 32rpx 10rpx;
color: $theme-text;
}
.groupNameInputArea {
position: relative;
.groupNameInput {
background-color: #fff;
height: 110rpx;
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
padding: 0 74rpx 0 32rpx;
color: #B747474;
font-size: 28rpx;
font-weight: 500;
line-height: 40rpx;
}
.groupName-input-clearBtn {
position: absolute;
right: 22rpx;
top: 40rpx;
width: 30rpx;
height: 30rpx;
}
}
}
}
</style>

View File

@ -0,0 +1,106 @@
<template>
<div class="outer-layer manage-group-silence-page">
<div class="root">
<ZPaging ref="zPaging" :show-scrollbar="false">
<template #top>
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
<div class="navBar-title flex flex-col items-center justify-center">
<span class="text-[34rpx] font-medium">
{{ $t('chat.settings.groupAdmin') }}
</span>
</div>
</tm-navbar>
</template>
<div class="manage-group-silence">
<span class="manage-group-silence-title text-[28rpx] font-regular">
{{ $t('chat.settings.groupAdmin') }}
</span>
<div
class="add-silence-member-btn chat-settings-card"
@click="toSelectMembersPage"
>
<img src="/src/static/image/chatSettings/add-btn.png" />
<span class="text-[28rpx] font-medium">
{{ $t('chat.manage.addAdmin') }}
</span>
</div>
</div>
</ZPaging>
</div>
</div>
</template>
<script setup>
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import { onLoad } from '@dcloudio/uni-app'
import { computed, onMounted, reactive } from 'vue'
import { useGroupStore, useDialogueStore } from '@/store'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const groupStore = useGroupStore()
const dialogueStore = useDialogueStore()
const dialogueParams = reactive({
adminList: computed(() => dialogueStore.getAdminList),
})
const state = reactive({
silenceAllBtn: null, //
})
onLoad((options) => {
console.log(options)
})
onMounted(() => {
console.log(dialogueParams.adminList)
})
//
const toSelectMembersPage = () => {
uni.navigateTo({
url: '/pages/chatSettings/groupManage/selectMembers?manageType=admin',
})
}
</script>
<style scoped lang="scss">
.outer-layer {
flex: 1;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-repeat: no-repeat;
}
.manage-group-silence {
padding: 20rpx 32rpx;
.manage-group-silence-title {
line-height: 40rpx;
color: #959598;
}
.add-silence-member-btn {
padding: 32rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
img {
width: 40rpx;
height: 40rpx;
margin: 0 22rpx 0 0;
}
span {
line-height: 40rpx;
color: $theme-text;
}
}
.chat-settings-card {
width: 100%;
background-color: #fff;
margin: 20rpx 0 0;
border-radius: 8rpx;
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
}
}
</style>

View File

@ -0,0 +1,58 @@
<template>
<div class="outer-layer manage-group-members-page">
<div class="root">
<ZPaging
ref="zPaging"
:show-scrollbar="false"
:use-virtual-list="true"
:virtual-list-col="5"
:auto="false"
:refresher-enabled="false"
:loading-more-enabled="false"
>
<template #top>
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
<div class="navBar-title flex flex-col items-center justify-center">
<span class="text-[34rpx] font-medium">
{{ $t('chat.settings.groupMember') }}
</span>
</div>
</tm-navbar>
</template>
<div class="group-members-list">
<groupMemberList
:memberList="talkParams?.memberList"
></groupMemberList>
</div>
</ZPaging>
</div>
</div>
</template>
<script setup>
import groupMemberList from '../components/groupMembersList.vue'
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
import { ref, computed, reactive } from 'vue'
import { useDialogueStore } from '@/store'
const dialogueStore = useDialogueStore()
const talkParams = reactive({
memberList: computed(() => dialogueStore.members),
})
const zPaging = ref()
useZPaging(zPaging)
</script>
<style scoped lang="scss">
.outer-layer {
flex: 1;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-repeat: no-repeat;
}
.group-members-list {
margin: 20rpx 32rpx;
padding: 0 0 32rpx;
background-color: #fff;
}
</style>

View File

@ -0,0 +1,129 @@
<template>
<div class="outer-layer manage-group-silence-page">
<div class="root">
<ZPaging ref="zPaging" :show-scrollbar="false">
<template #top>
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
<div class="navBar-title flex flex-col items-center justify-center">
<span class="text-[34rpx] font-medium">
{{ $t('chat.settings.groupGag') }}
</span>
</div>
</tm-navbar>
</template>
<div class="manage-group-silence">
<span class="manage-group-silence-title text-[28rpx] font-regular">
{{ $t('chat.manage.silenceMember') }}
</span>
<div
class="add-silence-member-btn chat-settings-card"
@click="toSelectMembersPage"
>
<img src="/src/static/image/chatSettings/add-btn.png" />
<span class="text-[28rpx] font-medium">
{{ $t('chat.manage.addSilenceMember') }}
</span>
</div>
<div class="silence-all-btn chat-settings-card">
<settingFormItem
:item="state.silenceAllBtn"
:sessionInfo="state?.sessionInfo"
@changeSwitch="changeSwitch"
></settingFormItem>
</div>
</div>
</ZPaging>
</div>
</div>
</template>
<script setup>
import settingFormItem from '../components/settingFormItem.vue'
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import { onLoad } from '@dcloudio/uni-app'
import { computed, onMounted, reactive } from 'vue'
import { useGroupStore, useDialogueStore } from '@/store'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const groupStore = useGroupStore()
const dialogueStore = useDialogueStore()
const dialogueParams = reactive({
silenceMembersList: computed(() => dialogueStore.getAdminList),
})
const state = reactive({
silenceAllBtn: null, //
})
onLoad((options) => {
console.log(options)
})
onMounted(() => {
console.log(dialogueParams.silenceMembersList)
state.silenceAllBtn = {
label: t('chat.manage.silenceAll'),
hasPointer: false,
value: '',
subValue: t('chat.manage.silenceAllHint'),
customInfo: 'switch',
}
})
//
const changeSwitch = (switchStatus, label) => {
console.log(switchStatus, label)
}
//
const toSelectMembersPage = () => {
uni.navigateTo({
url: '/pages/chatSettings/groupManage/selectMembers?manageType=silence',
})
}
</script>
<style scoped lang="scss">
.outer-layer {
flex: 1;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-repeat: no-repeat;
}
.manage-group-silence {
padding: 20rpx 32rpx;
.manage-group-silence-title {
line-height: 40rpx;
color: #959598;
}
.add-silence-member-btn {
padding: 32rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
img {
width: 40rpx;
height: 40rpx;
margin: 0 22rpx 0 0;
}
span {
line-height: 40rpx;
color: $theme-text;
}
}
.silence-all-btn {
padding: 32rpx;
}
.chat-settings-card {
width: 100%;
background-color: #fff;
margin: 20rpx 0 0;
border-radius: 8rpx;
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
}
}
</style>

View File

@ -0,0 +1,141 @@
<template>
<div class="outer-layer manage-notice-page">
<div class="root">
<ZPaging ref="zPaging" :show-scrollbar="false">
<template #top>
<tm-navbar :hideBack="true" hideHome title="" :leftWidth="220">
<template #left>
<div class="nav-bar-cancel-btn">
<span class="text-[34rpx] font-regular" @click="toPrevPage">
{{ $t('cancel') }}
</span>
</div>
</template>
<div class="navBar-title flex flex-col items-center justify-center">
<span class="text-[34rpx] font-medium">
{{ $t('chat.settings.groupNotice') }}
</span>
</div>
<template #right>
<div
class="nav-bar-done-btn"
:class="state.canDoComplete ? 'nav-bar-done-btn-can-do' : ''"
>
<span class="text-[34rpx] font-regular">
{{ $t('button.text.done') }}
</span>
</div>
</template>
</tm-navbar>
</template>
<div class="notice-text-area">
<wd-textarea
style="height: 100%;"
v-model="state.groupNotice"
:placeholder="$t('input.placeholder.enter')"
:maxlength="500"
:show-word-limit="true"
@input="inputGroupNotice"
/>
</div>
</ZPaging>
</div>
</div>
</template>
<script setup>
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import { reactive } from 'vue'
const state = reactive({
groupNotice: '', //
canDoComplete: false, //
})
//
const toPrevPage = () => {
uni.navigateBack({
delta: 1,
})
}
//
const inputGroupNotice = (e) => {
state.groupNotice = e
if (e.trim()) {
state.canDoComplete = true
} else {
state.canDoComplete = false
}
}
</script>
<style scoped lang="scss">
.outer-layer {
flex: 1;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-repeat: no-repeat;
}
.manage-notice-page {
::v-deep .zp-paging-container-content {
height: 100%;
}
.nav-bar-cancel-btn {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 0 0 0 42rpx;
span {
line-height: 40rpx;
}
}
.nav-bar-done-btn {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin: 0 36rpx 0 0;
padding: 6rpx 24rpx;
background-color: #f3f3f3;
border-radius: 8rpx;
span {
color: #bababa;
line-height: 40rpx;
}
}
.nav-bar-done-btn-can-do {
background-color: $theme-primary;
span {
color: #fff;
}
}
.notice-text-area {
padding: 20rpx 32rpx;
height: 100%;
::v-deep .wd-textarea {
height: 100%;
}
::v-deep .wd-textarea__value {
padding-right: 0;
height: 100%;
}
::v-deep .wd-textarea__inner {
height: 100%;
}
::v-deep .uni-textarea-compute {
div {
font-size: 32rpx;
font-weight: 400;
line-height: 44rpx;
}
}
}
}
</style>

View File

@ -0,0 +1,401 @@
<template>
<div class="outer-layer select-members-page">
<div class="root">
<ZPaging
ref="zPaging"
:show-scrollbar="false"
:use-virtual-list="true"
:virtual-list-col="5"
:refresher-enabled="false"
:loading-more-enabled="false"
@scroll="onScroll"
>
<template #top>
<tm-navbar
:hideBack="false"
hideHome
title=""
:leftWidth="220"
id="topArea"
>
<div class="navBar-title flex flex-col items-center justify-center">
<span
class="text-[34rpx] font-medium"
v-if="state.manageType === 'silence'"
>
{{ $t('chat.manage.addSilenceMember') }}
</span>
<span
class="text-[34rpx] font-medium"
v-if="state.manageType === 'admin'"
>
{{ $t('chat.manage.addAdmin') }}
</span>
<span
class="text-[34rpx] font-medium"
v-if="state.manageType === 'searchRecord'"
>
{{ $t('search.condition.member') }}
</span>
</div>
</tm-navbar>
</template>
<div class="select-members">
<div class="search-member">
<customInput
:searchText="state.searchText"
@inputSearchText="inputSearchText"
></customInput>
</div>
<div
class="member-list"
:style="{
padding: state.manageType === 'searchRecord' ? '20rpx 0 0' : '',
}"
>
<div class="member-list-alphabet-anchor-point">
<div
class="member-list-alphabet-anchor-point-each"
v-for="(alphabetItem, alphabetIndex) in state?.alphabet"
:key="alphabetIndex"
:style="{
margin: state?.alphabet?.length > 17 ? '0' : '',
}"
@click.stop="scrollToView(alphabetItem)"
>
<span
class="text-[32rpx] font-regular"
:style="{
color:
state.currentAlphabet === alphabetItem ? '#7A58DE' : '',
}"
>
{{ alphabetItem }}
</span>
</div>
</div>
<div
class="member-list-alphabet"
v-for="(alphabetItem, alphabetIndex) in state.resultMemberList"
:key="alphabetIndex"
>
<div
class="member-list-alphabet-key"
:style="{
padding:
state.manageType === 'searchRecord' ? '10rpx 30rpx' : '',
}"
v-if="alphabetItem?.memberList?.length > 0"
:id="alphabetItem.key"
:ref="
(el) => {
if (el) alphabetElementRefs[alphabetIndex] = el
}
"
>
<span class="text-[32rpx] font-regular">
{{ alphabetItem.key }}
</span>
</div>
<div v-if="alphabetItem?.memberList?.length > 0">
<div
class="member-list-each"
v-for="(item, index) in alphabetItem?.memberList"
:key="index"
>
<tm-checkbox-group v-model="item.checkArr">
<selectMemberItem
:memberItem="item"
@clickItem="handleClickItem(item)"
:manageType="state.manageType"
:itemStyle="
state.manageType === 'searchRecord' ? 'list' : 'card'
"
>
<template
#left
v-if="state.manageType !== 'searchRecord'"
>
<tm-checkbox
:round="10"
color="#46299d"
:value="item.id"
:disabled="item.is_mute === 1"
></tm-checkbox>
</template>
</selectMemberItem>
</tm-checkbox-group>
</div>
</div>
</div>
</div>
</div>
<template #bottom v-if="state.manageType !== 'searchRecord'">
<customBtn
:isBottom="true"
:btnText="$t('ok')"
@clickBtn="confirmSilenceMember"
></customBtn>
</template>
</ZPaging>
</div>
</div>
</template>
<script setup>
import customInput from '@/components/custom-input/custom-input.vue'
import selectMemberItem from '../components/select-member-item.vue'
import customBtn from '@/components/custom-btn/custom-btn.vue'
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
import { computed, onMounted, reactive, ref, watch, nextTick } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useDialogueStore } from '@/store'
const zPaging = ref()
useZPaging(zPaging)
const dialogueStore = useDialogueStore()
const state = reactive({
searchText: '', //
manageType: '', //
alphabet: [], //A-Z
resultMemberList: [], //A-Z
currentAlphabet: 'A', //A-Z
scrollDirection: '', //
isAssign: false, //view
})
const dialogueParams = reactive({
memberList: computed(() => {
const lowerCaseSearchText = state?.searchText.toLowerCase()
return dialogueStore.members.filter((item) =>
state?.searchText
? item.nickname.toLowerCase().includes(lowerCaseSearchText)
: true,
)
}),
receiverId: computed(() => dialogueStore.talk.receiver_id),
})
watch(
() => dialogueParams?.memberList,
(newMemberList) => {
assembleAlphabetMmeberList(newMemberList)
},
{ deep: true },
)
//A-Z tag
const alphabetElementRefs = ref([])
//
let observer
onLoad((options) => {
console.log(options)
if (options.manageType) {
state.manageType = options.manageType
assembleAlphabetMmeberList(dialogueParams?.memberList)
}
})
onMounted(() => {
dialogueParams.memberList.forEach((ele) => {
ele.checkArr = []
})
const options = {
root: null, // 使
threshold: 1.0, // 100%
}
observer = new IntersectionObserver(handleIntersection, options)
nextTick(() => {
watch(
alphabetElementRefs,
(newAlphabetElementRefs) => {
if (Array.isArray(newAlphabetElementRefs)) {
newAlphabetElementRefs.forEach((el, index) => {
observeElement(el, index)
})
}
},
{ immediate: true, deep: true },
)
if (alphabetElementRefs.value.length > 0) {
alphabetElementRefs.value.forEach((el, index) =>
observeElement(el, index),
)
}
})
})
//
const handleIntersection = (entries) => {
if (state.isAssign) {
state.isAssign = false
return
}
entries.forEach((entry) => {
if (!entry.isIntersecting && state.scrollDirection === 'down') {
state.currentAlphabet = entry.target.id
} else if (entry.isIntersecting && state.scrollDirection === 'up') {
if (state?.alphabet?.length > 1) {
state?.alphabet.forEach((item, index) => {
if (item === entry.target.id && index > 0) {
state.currentAlphabet = state?.alphabet[index - 1]
}
})
} else {
state.currentAlphabet = entry.target.id
}
}
})
}
//
const observeElement = (el, index) => {
if (el && observer) {
observer.observe(el)
}
}
//
const inputSearchText = (e) => {
// console.log(e)
state.searchText = e
}
//item
const handleClickItem = (item) => {
dialogueParams.memberList.forEach((ele) => {
if (ele.id == item.id) {
ele.checkArr = ele.checkArr?.length > 0 ? [] : [item.id]
}
})
}
//
const confirmSilenceMember = () => {
let selectedUserIds = ''
dialogueParams.memberList.forEach((ele) => {
if (ele.checkArr?.length > 0) {
if (!selectedUserIds) {
selectedUserIds = ele.checkArr[0]
} else {
selectedUserIds += ',' + ele.checkArr[0]
}
}
})
console.log(selectedUserIds)
if (selectedUserIds) {
let params = {
mode: 1, //12
group_id: dialogueParams.receiverId, //id
user_ids: selectedUserIds, //ids
}
console.log(params)
}
}
//A-Z
const assembleAlphabetMmeberList = (newMemberList) => {
if (state.manageType === 'searchRecord') {
const resultMemberList = ref([])
const alphabet = Array.from({ length: 26 }, (_, i) =>
String.fromCharCode(i + 65),
)
let tempAlphabet = []
alphabet.forEach((letter) => {
const matchedItems = newMemberList.filter((item) => item.key === letter)
if (matchedItems.length > 0) {
tempAlphabet.push(letter)
}
resultMemberList.value.push({
key: letter,
memberList: matchedItems.length ? matchedItems : [],
})
})
state.alphabet = tempAlphabet
state.resultMemberList = resultMemberList
} else {
state.resultMemberList = [
{
key: '',
memberList: newMemberList,
},
]
}
}
//view
const scrollToView = (alphabet) => {
state.currentAlphabet = alphabet
state.isAssign = true
console.log()
zPaging.value?.scrollIntoViewById(
alphabet,
document.getElementById('topArea').clientHeight
? document.getElementById('topArea').clientHeight - 1
: 80,
)
}
//
const onScroll = (e) => {
if (e.detail.deltaY < 0) {
state.scrollDirection = 'down'
} else if (e.detail.deltaY > 0) {
state.scrollDirection = 'up'
} else {
state.scrollDirection = ''
}
}
</script>
<style scoped lang="scss">
.outer-layer {
flex: 1;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-repeat: no-repeat;
}
.select-members {
padding: 20rpx 32rpx;
.search-member {
padding: 22rpx 16rpx;
background-color: #fff;
}
.member-list {
.member-list-alphabet-anchor-point {
position: fixed;
right: 32rpx;
top: 0;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.member-list-alphabet-anchor-point-each {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 0 0 14rpx;
span {
width: 52rpx;
text-align: center;
line-height: 44rpx;
color: $theme-text;
}
}
}
.member-list-alphabet {
.member-list-alphabet-key {
background-color: #f3f3f3;
span {
line-height: 44rpx;
color: $theme-text;
}
}
}
}
}
</style>

View File

@ -0,0 +1,639 @@
<template>
<div class="outer-layer chat-settings-page">
<div class="root">
<ZPaging ref="zPaging" :show-scrollbar="false">
<template #top>
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
<div class="navBar-title flex flex-col items-center justify-center">
<span class="text-[34rpx] font-medium">
{{ $t('index.chat.settings') }}
</span>
</div>
</tm-navbar>
</template>
<div class="chat-settings">
<div class="chat-group-base-infos chat-settings-card">
<div class="base-info-avatar" @click="toEditAvatarPage">
<img :src="groupAvatar" />
</div>
<div class="base-info">
<div class="base-info-name">
<span class="text-[32rpx] font-medium">{{ groupName }}</span>
<span class="base-info_num text-[32rpx] font-medium">
{{ '' + groupNum + '' }}
</span>
</div>
<div
class="base-info-tag"
:style="{
borderColor:
groupTypeMapping[groupParams?.groupInfo?.group_type]
?.result_type_color,
}"
>
<span
class="text-[24rpx] font-medium"
:style="{
color:
groupTypeMapping[groupParams?.groupInfo?.group_type]
?.result_type_color,
}"
>
{{ groupType }}
</span>
</div>
</div>
<div class="base-info-edit" @click="toEditGroupInfoPage">
<img src="/src/static/image/chatSettings/edit-btn.png" />
</div>
</div>
<div class="chat-group-members chat-group-infos chat-settings-card">
<div
class="chat-group-infos-each"
v-for="(item, index) in state.chatGroupMembers"
:key="index"
>
<settingFormItem
:item="item"
@toManagePage="toManagePage"
></settingFormItem>
<groupMemberList
:memberList="dialogueParams?.memberList"
:memberListsLimit="15"
></groupMemberList>
</div>
</div>
<div class="chat-group-infos chat-settings-card">
<div
class="chat-group-infos-each"
v-for="(item, index) in state.chatGroupInfos"
:key="index"
>
<settingFormItem
:item="item"
@toManagePage="toManagePage"
></settingFormItem>
</div>
</div>
<div class="chat-records-search chat-settings-card">
<customInput></customInput>
<div class="record-search-types">
<div
class="record-search-types-each"
v-for="(item, index) in state.recordSearchTypeList"
:key="index"
@click="toSearchByConditionPage(index)"
>
<img class="record-search-types-icon" :src="item.typeIcon" />
<span class="text-[24rpx] font-regular">{{ item.value }}</span>
</div>
</div>
</div>
<div class="chat-group-infos chat-settings-card">
<div
class="chat-group-infos-each"
v-for="(item, index) in state.chatSettings"
:key="index"
>
<settingFormItem
:item="item"
@toManagePage="toManagePage"
:sessionInfo="state?.sessionInfo"
@changeSwitch="changeSwitch"
></settingFormItem>
</div>
</div>
<div class="chat-group-infos chat-settings-card">
<div
class="chat-group-infos-each"
v-for="(item, index) in state.chatManagement"
:key="index"
>
<settingFormItem
:item="item"
@toManagePage="toManagePage"
></settingFormItem>
</div>
</div>
<div class="clear-chat-record-btn chat-settings-card">
<div class="clear-chat-record-btn-each">
<span class="text-[32rpx] font-regular">
{{ $t('chat.settings.clearChatRecord') }}
</span>
</div>
<div class="clear-chat-record-btn-each">
<span class="text-[32rpx] font-regular">
{{ $t('group.disband.btn') }}
</span>
</div>
<div class="clear-chat-record-btn-each">
<span class="text-[32rpx] font-regular">
{{ $t('group.quit.btn') }}
</span>
</div>
</div>
</div>
</ZPaging>
</div>
</div>
</template>
<script setup>
import zu4992 from '@/static/image/chatList/zu4992@2x.png'
import zu4991 from '@/static/image/chatList/zu4991@2x.png'
import zu4989 from '@/static/image/chatList/zu4989@2x.png'
import zu5296 from '@/static/image/chatList/zu5296@2x.png'
import recordSearchTypeIcon_groupMember from '@/static/image/chatSettings/recordSearchTypeGroupMembers.png'
import recordSearchTypeIcon_date from '@/static/image/chatSettings/recordSearchTypeDate.png'
import recordSearchTypeIcon_imgAndVideo from '@/static/image/chatSettings/recordSearchTypeImgAndVideo.png'
import recordSearchTypeIcon_files from '@/static/image/chatSettings/recordSearchTypeFiles.png'
import recordSearchTypeIcon_link from '@/static/image/chatSettings/recordSearchTypeLink.png'
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import settingFormItem from './components/settingFormItem.vue'
import groupMemberList from './components/groupMembersList.vue'
import { computed, onMounted, reactive } from 'vue'
import {
useUserStore,
useTalkStore,
useDialogueStore,
useGroupStore,
} from '@/store'
import { onLoad } from '@dcloudio/uni-app'
import { ServeTopTalkList, ServeSetNotDisturb } from '@/api/chat/index'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
import customInput from '@/components/custom-input/custom-input.vue'
const userStore = useUserStore()
const talkStore = useTalkStore()
const dialogueStore = useDialogueStore()
const dialogueParams = reactive({
uid: computed(() => userStore.uid),
index_name: computed(() => dialogueStore.index_name),
type: computed(() => dialogueStore.talk.talk_type),
receiver_id: computed(() => dialogueStore.talk.receiver_id),
username: computed(() => dialogueStore.talk.username),
online: computed(() => dialogueStore.online),
keyboard: computed(() => dialogueStore.keyboard),
num: computed(() => dialogueStore.members.length),
memberList: computed(() => dialogueStore.members),
})
const talkParams = reactive({
topItems: computed(() => talkStore.topItems),
disturbItems: computed(() => talkStore.disturbItems),
})
const groupStore = useGroupStore()
const groupParams = reactive({
groupInfo: computed(() => groupStore.groupInfo),
groupNotice: computed(() => groupStore.groupNotice),
})
const state = reactive({
chatGroupMembers: [], //form-item
chatGroupInfos: [], //
recordSearchTypeList: [], //
chatSettings: [], //
chatManagement: [], //
groupId: '', //id
sessionId: '', //id
})
onLoad(async (options) => {
console.log(dialogueParams)
if (options.groupId) {
console.log(options.groupId)
state.groupId = Number(options.groupId)
await groupStore.ServeGroupDetail()
await groupStore.ServeGetGroupNotices()
updateGroupInfos()
}
if (options.sessionId) {
state.sessionId = Number(options.sessionId)
if (talkParams.topItems.length > 0) {
talkParams.topItems.forEach((item) => {
if (item.id == options.sessionId) {
state.sessionInfo = item
}
})
}
if (talkParams.disturbItems.length > 0) {
talkParams.disturbItems.forEach((item) => {
if (item.id == options.sessionId) {
state.sessionInfo = item
}
})
}
}
})
onMounted(() => {
updateGroupInfos()
state.recordSearchTypeList = [
{
value: t('chat.settings.groupMember'),
typeIcon: recordSearchTypeIcon_groupMember,
},
{
value: t('record.searchType.date'),
typeIcon: recordSearchTypeIcon_date,
},
{
value: t('record.searchType.imgAndVideo'),
typeIcon: recordSearchTypeIcon_imgAndVideo,
},
{
value: t('record.searchType.files'),
typeIcon: recordSearchTypeIcon_files,
},
{
value: t('record.searchType.link'),
typeIcon: recordSearchTypeIcon_link,
},
]
state.chatSettings = [
{
label: t('chat.settings.topSession'),
hasPointer: false,
value: '',
subValue: '',
customInfo: 'switch',
},
{
label: t('chat.settings.messageNoDisturb'),
hasPointer: false,
value: '',
subValue: '',
customInfo: 'switch',
},
]
state.chatManagement = [
{
label: t('chat.settings.groupGag'),
hasPointer: true,
value: '',
subValue: '',
customInfo: '',
},
{
label: t('chat.settings.groupAdmin'),
hasPointer: true,
value: '',
subValue: '总经理室-总经理,苏州站-出纳,常熟站…',
customInfo: '',
},
]
})
//
const groupAvatar = computed(() => {
return (
groupParams?.groupInfo?.avatar ||
groupTypeMapping[groupParams?.groupInfo?.group_type]?.defaultImg
)
})
//
const groupName = computed(() => {
return groupParams?.groupInfo?.group_name
})
//
const groupNum = computed(() => {
return groupParams?.groupInfo?.group_num || 0
})
// -groupType
const groupTypeMapping = {
0: {
defaultImg: 'textImg',
},
1: {
defaultImg: zu4992,
},
2: {
result_type: t('index.mine.department'),
result_type_color: '#377EC6',
defaultImg: zu4989,
},
3: {
result_type: t('index.mine.project'),
result_type_color: '#C1681C',
defaultImg: zu4991,
},
4: {
result_type: t('index.type.company'),
result_type_color: '#7A58DE',
defaultImg: zu5296,
},
}
//
const groupType = computed(() => {
return groupTypeMapping[groupParams?.groupInfo?.group_type]?.result_type || ''
})
//
const updateGroupInfos = () => {
state.chatGroupMembers = [
{
label: t('chat.settings.groupMember'),
hasPointer: true,
value: '全部(' + groupNum.value + '',
subValue: '',
customInfo: '',
},
]
state.chatGroupInfos = [
{
label: t('chat.settings.groupName'),
hasPointer: true,
value: groupName.value,
subValue: '',
customInfo: '',
},
{
label: t('chat.settings.groupNotice'),
hasPointer: true,
value: '',
subValue: groupParams.groupNotice[0],
customInfo: '',
},
{
label: t('chat.settings.groupType'),
hasPointer: false,
value: groupType.value + '群',
subValue: '',
customInfo: '',
},
]
}
//
const toEditGroupInfoPage = () => {
uni.navigateTo({
url:
'/pages/chatSettings/groupManage/editGroupName?groupAvatar=' +
groupAvatar.value,
})
}
//
const toEditAvatarPage = () => {
uni.navigateTo({
url:
'/pages/chatSettings/groupManage/editAvatar?groupAvatar=' +
groupAvatar.value,
})
}
//
const toManagePage = (label) => {
console.log(label)
if (label) {
if (label === t('chat.settings.groupName')) {
uni.navigateTo({
url:
'/pages/chatSettings/groupManage/editGroupName?groupAvatar=' +
groupAvatar.value,
})
} else if (label === t('chat.settings.groupNotice')) {
uni.navigateTo({
url: '/pages/chatSettings/groupManage/manageNotice',
})
} else if (label === t('chat.settings.groupMember')) {
uni.navigateTo({
url:
'/pages/chatSettings/groupManage/manageGroupMembers?groupId=' +
state.groupId,
})
} else if (label === t('chat.settings.groupGag')) {
uni.navigateTo({
url: '/pages/chatSettings/groupManage/manageGroupSilence',
})
} else if (label === t('chat.settings.groupAdmin')) {
uni.navigateTo({
url: '/pages/chatSettings/groupManage/manageGroupAdmin',
})
}
}
}
//
const toSearchByConditionPage = (flag) => {
let condition = ''
if (flag == 0) {
uni.navigateTo({
url:
'/pages/chatSettings/groupManage/selectMembers?manageType=searchRecord',
})
} else {
if (flag == 1) {
condition = 'date'
}
uni.navigateTo({
url:
'/pages/search/searchByCondition/index?condition=' +
condition +
'&receiver_id=' +
state.groupId,
})
}
}
//
const changeSwitch = (switchStatus, label) => {
let params
let resp
if (label == t('chat.settings.topSession')) {
params = {
list_id: state.sessionId, //id
type: switchStatus ? 1 : 2,
}
resp = ServeTopTalkList(params)
} else if (label == t('chat.settings.messageNoDisturb')) {
params = {
talk_type: dialogueParams.type, //12
receiver_id: dialogueParams.receiver_id, //idid
is_disturb: switchStatus ? 1 : 0, //01
}
resp = ServeSetNotDisturb(params)
}
console.log(resp)
resp.then(({ code, data }) => {
console.log(data)
if (code == 200) {
if (label == t('chat.settings.topSession')) {
talkStore.updateItem({
index_name: dialogueParams.index_name,
is_top: switchStatus ? 1 : 2,
})
} else if (label == t('chat.settings.messageNoDisturb')) {
talkStore.updateItem({
index_name: dialogueParams.index_name,
is_disturb: switchStatus ? 1 : 0,
})
}
} else {
}
})
resp.catch(() => {})
}
</script>
<style scoped lang="scss">
.outer-layer {
flex: 1;
background-image: url('@/static/image/clockIn/z3280@3x.png');
background-size: cover;
background-repeat: no-repeat;
}
.chat-settings-page {
.navBar-title {
span {
line-height: 48rpx;
}
}
.chat-settings {
padding: 0 32rpx 36rpx;
width: 100%;
.chat-settings-card {
width: 100%;
background-color: #fff;
margin: 20rpx 0 0;
border-radius: 8rpx;
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
}
.chat-group-base-infos {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 32rpx 40rpx 32rpx 20rpx;
.base-info-avatar {
width: 96rpx;
height: 96rpx;
flex-shrink: 0;
border-radius: 50%;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
.base-info {
width: 100%;
margin: 0 30rpx;
.base-info-name {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
span {
line-height: 44rpx;
color: $theme-text;
}
.base-info_num {
line-height: 44rpx;
}
}
.base-info-tag {
border: 2rpx solid #7a58de;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 2rpx 14rpx;
margin: 10rpx 0 0;
border-radius: 8rpx;
width: 80rpx;
span {
flex-shrink: 0;
color: #7a58de;
line-height: 34rpx;
}
}
}
.base-info-edit {
width: 36rpx;
height: 36rpx;
flex-shrink: 0;
img {
width: 100%;
height: 100%;
}
}
}
.chat-group-members {
}
.chat-group-infos {
padding: 0 16rpx;
.chat-group-infos-each {
padding: 32rpx 14rpx;
border-bottom: 1px solid $theme-border-color;
}
.chat-group-infos-each:last-child {
border-bottom: 0;
}
}
.chat-records-search {
padding: 22rpx 16rpx;
.record-search-types {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
.record-search-types-each {
width: calc(100% / 4);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 36rpx 0 0;
.record-search-types-icon {
width: 106rpx;
height: 106rpx;
border-radius: 50%;
background-color: #f9f9f9;
}
span {
line-height: 34px;
color: #666666;
}
}
}
}
.clear-chat-record-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.clear-chat-record-btn-each {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 32rpx 16rpx;
width: calc(100% - 32rpx);
border-bottom: 1px solid $theme-border-color;
span {
line-height: 44rpx;
color: #cf3050;
}
}
.clear-chat-record-btn-each:last-child {
border-bottom: 0;
}
}
}
}
</style>

View File

@ -8,7 +8,7 @@
</div> </div>
<template v-slot:right> <template v-slot:right>
<div class="mr-[36rpx]"> <div class="mr-[36rpx]">
<tm-icon color="rgb(51, 51, 51)" :font-size="36" name="tmicon-gengduo"></tm-icon> <tm-icon color="rgb(51, 51, 51)" :font-size="36" name="tmicon-gengduo" @click="toChatSettingsPage"></tm-icon>
</div> </div>
</template> </template>
</tm-navbar> </tm-navbar>
@ -228,8 +228,7 @@ import zu6053 from "@/static/image/chatList/zu6053@2x.png"
import deepBubble from "@/components/deep-bubble/deep-bubble.vue" import deepBubble from "@/components/deep-bubble/deep-bubble.vue"
import {isRevoke } from './menu' import {isRevoke } from './menu'
import useConfirm from '@/components/x-confirm/useConfirm.js' import useConfirm from '@/components/x-confirm/useConfirm.js'
import { onLoad as uniOnload } from '@dcloudio/uni-app'
Quill.register('formats/emoji', EmojiBlot) Quill.register('formats/emoji', EmojiBlot)
@ -260,6 +259,13 @@ const state = ref({
isOpenFilePanel: false, isOpenFilePanel: false,
showWin: false, showWin: false,
onfocusItem: null, onfocusItem: null,
sessionId: ''
})
uniOnload((options) => {
if (options.sessionId) {
state.sessionId = options.sessionId
}
}) })
const handleEmojiPanel = () => { const handleEmojiPanel = () => {
@ -680,6 +686,13 @@ const initData = async () => {
zpagingRef.value?.complete(records.value) zpagingRef.value?.complete(records.value)
} }
//
const toChatSettingsPage = () => {
uni.navigateTo({
url: '/pages/chatSettings/index?groupId=' + talkParams?.receiver_id + '&sessionId=' + state.sessionId
})
}
onMounted(async () => { onMounted(async () => {
initData() initData()
}) })

View File

@ -139,7 +139,7 @@ const cellClick = () => {
}); });
} }
uni.navigateTo({ uni.navigateTo({
url: "/pages/dialog/index", url: '/pages/dialog/index?sessionId=' + props.data.id,
}); });
}; };

View File

@ -57,7 +57,7 @@
</tm-navbar> </tm-navbar>
</div> </div>
<div class="root"> <div class="root">
<div class="searchRoot"> <div class="searchRoot" @click="toSearchPage">
<tm-input <tm-input
placeholder="请输入…" placeholder="请输入…"
color="#F9F9FD" color="#F9F9FD"
@ -111,6 +111,12 @@ const creatGroupChat = () => {
}); });
}; };
const toSearchPage = () => {
uni.navigateTo({
url: "/pages/search/index",
});
};
watch( watch(
() => talkStore, () => talkStore,
(newValue, oldValue) => { (newValue, oldValue) => {

View File

@ -0,0 +1,70 @@
<template>
<span>
<template v-for="(part, index) in parts" :key="index">
<span v-if="part.highlighted" :class="highlightClass">
{{ part.text }}
</span>
<span v-else>{{ part.text }}</span>
</template>
</span>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
text: {
type: String,
required: true,
},
searchText: {
type: String,
default: '',
},
highlightClass: {
type: String,
default: 'highlight',
},
})
const escapedSearchText = computed(() =>
String(props.searchText).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'),
)
const pattern = computed(() => new RegExp(escapedSearchText.value, 'gi'))
const parts = computed(() => {
if (!props.searchText || !props.text)
return [{ text: props.text, highlighted: false }];
const result = [];
let currentIndex = 0;
const escapedSearchTextValue = escapedSearchText.value;
const searchPattern = new RegExp(`(${escapedSearchTextValue})`, 'gi');
props.text.replace(searchPattern, (match, p1, offset) => {
//
if (currentIndex < offset) {
result.push({ text: props.text.slice(currentIndex, offset), highlighted: false });
}
//
result.push({ text: p1, highlighted: true });
//
currentIndex = offset + p1.length;
return p1; // replace
});
//
if (currentIndex < props.text.length) {
result.push({ text: props.text.slice(currentIndex), highlighted: false });
}
return result;
});
</script>
<style scoped>
.highlight {
color: #7a58de;
}
</style>

View File

@ -0,0 +1,331 @@
<template>
<div
class="search-item"
v-if="resultName"
:style="
props.searchResultKey === 'talk_record_infos_receiver'
? 'margin: 24rpx 0 0'
: ''
"
>
<div class="avatar-img">
<img v-if="avatarImg !== 'textImg'" :src="avatarImg" />
<span v-if="avatarImg === 'textImg'" class="text-[32rpx] font-bold">
{{ imgText }}
</span>
</div>
<div class="result-info">
<div
class="info-name"
:class="searchRecordDetail ? 'info-name-searchRecordDetail' : ''"
>
<HighlightText
:class="
searchRecordDetail
? 'text-[24rpx] font-medium'
: 'text-[32rpx] font-medium'
"
:text="resultName"
:searchText="props.searchText"
/>
<div class="info_num" v-if="groupNum">
<span class="text-[32rpx] font-medium">
{{ '' + groupNum + '' }}
</span>
</div>
<div
class="info-tag"
v-if="resultType && !searchRecordDetail"
:style="'border-color:' + resultTypeColor"
>
<span
class="text-[24rpx] font-medium"
:style="'color:' + resultTypeColor"
>
{{ resultType }}
</span>
</div>
<div v-if="searchRecordDetail && chatRecordCreatedAt">
<span class="text-[24rpx] font-medium">
{{ chatRecordCreatedAt }}
</span>
</div>
</div>
<div
class="info-detail"
v-if="resultDetail"
:class="searchRecordDetail ? 'info-detail-searchRecordDetail' : ''"
>
<HighlightText
class="text-[28rpx] font-regular"
:text="resultDetail"
:searchText="props.searchText"
/>
</div>
</div>
<div class="search-item-pointer" v-if="pointerIconSrc">
<img :src="pointerIconSrc" />
</div>
</div>
</template>
<script setup>
import zu4992 from '@/static/image/chatList/zu4992@2x.png'
import zu4991 from '@/static/image/chatList/zu4991@2x.png'
import zu4989 from '@/static/image/chatList/zu4989@2x.png'
import zu5296 from '@/static/image/chatList/zu5296@2x.png'
import {
ref,
watch,
computed,
onMounted,
onUnmounted,
reactive,
defineProps,
} from 'vue'
import HighlightText from './highLightText.vue'
import { useI18n } from 'vue-i18n'
import { beautifyTime } from '@/utils/datetime'
const { t } = useI18n()
const props = defineProps({
searchItem: Object | Number,
searchResultKey: String,
searchText: String, //
searchRecordDetail: Boolean, //
pointerIconSrc: String, //
})
// -
const keyMapping = {
user_infos: { avatar: 'avatar', name: 'nickname' },
group_infos: { avatar: 'avatar', name: 'name', group_num: 'group_num' },
group_member_infos: {
avatar: 'group_avatar',
name: 'group_name',
detailKey: 'user_name',
group_num: 'group_num',
},
combinedGroup: {
avatar: props.searchItem?.groupTempType
? props.searchItem?.groupTempType === 'group_infos'
? 'avatar'
: props.searchItem?.groupTempType === 'group_member_infos'
? 'group_avatar'
: ''
: '',
name: props.searchItem?.groupTempType
? props.searchItem?.groupTempType === 'group_infos'
? 'name'
: props.searchItem?.groupTempType === 'group_member_infos'
? 'group_name'
: ''
: '',
detailKey: props.searchItem?.groupTempType
? props.searchItem?.groupTempType === 'group_member_infos'
? 'user_name'
: ''
: '',
group_num: props.searchItem?.groupTempType
? props.searchItem?.groupTempType === 'group_infos'
? 'group_num'
: props.searchItem?.groupTempType === 'group_member_infos'
? 'group_num'
: ''
: '',
},
general_infos: {
avatar: 'receiver_avatar',
name: 'receiver_name',
detailKey: 'count',
group_num: 'group_num',
},
talk_record_infos: {
avatar: 'user_avatar',
name: 'user_name',
detailKey: 'extra',
created_at: 'created_at',
},
talk_record_infos_receiver: {
avatar: 'receiver_avatar',
name: 'receiver_name',
group_num: 'group_num',
},
}
//key
const getKeyValue = (keys) => {
let keyValue = ''
if (keys) {
keyValue = props?.searchItem ? props?.searchItem[keys] : ''
}
return keyValue
}
//
const avatarImg = computed(() => {
let avatar = getKeyValue(keyMapping[props.searchResultKey]?.avatar)
if (!avatar) {
avatar = groupTypeMapping[props.searchItem?.group_type]?.defaultImg
}
return avatar
})
//
const resultName = computed(() => {
return getKeyValue(keyMapping[props.searchResultKey]?.name)
})
//
const imgText = computed(() => {
return resultName.value.length >= 2
? resultName.value.slice(-2)
: resultName.value
})
// -groupType
const groupTypeMapping = {
0: {
defaultImg: 'textImg',
},
1: {
defaultImg: zu4992,
},
2: {
result_type: t('index.mine.department'),
result_type_color: '#377EC6',
defaultImg: zu4989,
},
3: {
result_type: t('index.mine.project'),
result_type_color: '#C1681C',
defaultImg: zu4991,
},
4: {
result_type: t('index.type.company'),
result_type_color: '#7A58DE',
defaultImg: zu5296,
},
}
//
const groupNum = computed(() => {
return getKeyValue(keyMapping[props.searchResultKey]?.group_num)
})
//tag
const resultType = computed(() => {
return groupTypeMapping[props.searchItem?.group_type]?.result_type
})
//tag
const resultTypeColor = computed(() => {
return groupTypeMapping[props.searchItem?.group_type]?.result_type_color
})
//-
const chatRecordCreatedAt = computed(() => {
let created_at = getKeyValue(keyMapping[props.searchResultKey]?.created_at)
return beautifyTime(created_at)
})
//
const resultDetail = computed(() => {
let result_detail =
props.searchItem[keyMapping[props.searchResultKey]?.detailKey]
switch (keyMapping[props.searchResultKey]?.detailKey) {
case 'count':
result_detail = result_detail + t('search.chat.count')
break
case 'user_name':
result_detail = t('search.result.include') + result_detail
break
case 'extra':
result_detail = props.searchItem?.extra
break
default:
result_detail = ''
}
return result_detail
})
</script>
<style lang="scss" scoped>
.search-item:nth-child(1) {
border-top: 1px solid $theme-border-color;
}
.search-item {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
padding: 22rpx 0 24rpx;
border-bottom: 1px solid $theme-border-color;
.avatar-img {
width: 96rpx;
height: 96rpx;
margin: 0 20rpx 0 0;
border-radius: 50%;
overflow: hidden;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
background: linear-gradient(to right, #674bbc, #46299d);
flex-shrink: 0;
img {
width: 100%;
height: 100%;
}
span {
color: #fff;
line-height: 44rpx;
}
}
.result-info {
width: 100%;
.info-name {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
span {
color: $theme-text;
line-height: 44rpx;
}
.info-tag {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 2rpx 14rpx;
border: 2rpx solid #000;
border-radius: 6rpx;
span {
line-height: 34rpx;
}
}
}
.info-name-searchRecordDetail {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
span {
color: $theme-hint-text;
line-height: 34rpx;
}
}
.info-detail {
span {
color: $theme-hint-text;
line-height: 40rpx;
}
}
.info-detail-searchRecordDetail {
span {
color: $theme-text;
}
}
}
.search-item-pointer {
width: 11rpx;
height: 18rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
img {
width: 100%;
height: 100%;
}
}
}
</style>

View File

@ -0,0 +1,419 @@
<template>
<div class="search-list">
<ZPaging
ref="zPaging"
:show-scrollbar="false"
v-model="state.searchResultList"
@query="queryAllSearch"
:default-page-no="state.pageNum"
:default-page-size="props.searchResultPageSize"
:loading-more-default-as-loading="true"
:inside-more="true"
:empty-view-img="'/src/static//image/search/search-no-data.png'"
:empty-view-text="$t('search.hint')"
:empty-view-img-style="{ width: '476rpx', height: '261rpx' }"
:empty-view-title-style="{
color: '#999999',
margin: '-20rpx 0 0',
'line-height': '40rpx',
'font-size': '28rpx',
'font-weight': 400,
}"
>
<template #top>
<div class="searchRoot">
<customInput
:searchText="state.searchText"
:first_talk_record_infos="state.first_talk_record_infos"
@inputSearchText="inputSearchText"
></customInput>
<span
class="searchRoot_cancelBtn text-[32rpx] font-medium"
@click="cancelSearch"
>
{{ $t('cancel') }}
</span>
</div>
</template>
<div class="search-record-detail" v-if="props.searchRecordDetail">
<searchItem
@click="
clickSearchItem(
'talk_record_infos_receiver',
state?.first_talk_record_infos,
)
"
searchResultKey="talk_record_infos_receiver"
:searchItem="state?.first_talk_record_infos"
:pointerIconSrc="pointerIconSrc"
></searchItem>
</div>
<div
class="search-result"
:style="
!state.searchText ? 'align-items:center;justify-content:center;' : ''
"
>
<div class="search-result-list">
<div
class="search-result-each-part"
v-for="(searchResultValue,
searchResultKey,
searchResultIndex) in state.searchResult"
:key="searchResultKey"
>
<div
class="search-result-part"
v-if="
Array.isArray(state?.searchResult[searchResultKey]) &&
state?.searchResult[searchResultKey].length > 0 &&
searchResultKey !== 'group_infos' &&
searchResultKey !== 'group_member_infos'
"
>
<div class="result-title">
<span class="text-[28rpx] font-regular">
{{ getResultKeysValue(searchResultKey) }}
</span>
</div>
<div class="result-list">
<div
class="result-list-each"
v-for="(item, index) in state?.searchResult[searchResultKey]"
:key="index"
>
<searchItem
@click="clickSearchItem(searchResultKey, item)"
v-if="(props.listLimit && index < 3) || !props.listLimit"
:searchResultKey="searchResultKey"
:searchItem="item"
:searchText="state.searchText"
:searchRecordDetail="props.searchRecordDetail"
></searchItem>
</div>
</div>
<div
class="result-has-more"
v-if="getHasMoreResult(searchResultKey)"
@click="toMoreResultPage(searchResultKey)"
>
<span class="text-[28rpx] font-regular">
{{ getHasMoreResult(searchResultKey) }}
</span>
</div>
</div>
</div>
</div>
</div>
</ZPaging>
</div>
</template>
<script setup>
import customInput from '@/components/custom-input/custom-input.vue'
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
import searchItem from './searchItem.vue'
import { useI18n } from 'vue-i18n'
import { ref, reactive, defineEmits, defineProps, onMounted } from 'vue'
import pointerIconSrc from '@/static/image/search/search-item-pointer.png'
const zPaging = ref()
useZPaging(zPaging)
const emits = defineEmits([
'toMoreResultPage',
'lastIdChange',
'clickSearchItem',
])
const state = reactive({
searchText: '', //
searchResultList: [], //
searchResult: null, //
pageNum: 1, //
})
const props = defineProps({
searchResultPageSize: Number, //
listLimit: Boolean, //
apiParams: String, //
apiRequest: Function, //
searchText: String, //
isPagination: Boolean, //
searchRecordDetail: Boolean, //
first_talk_record_infos: Object, //
})
const { t } = useI18n()
onMounted(() => {
if (props.searchText) {
state.searchText = props.searchText
}
})
//
const inputSearchText = (e) => {
// console.log(e)
if (e.trim() != state.searchText.trim()) {
state.pageNum = 1
emits('lastIdChange', 0, 0, 0)
}
state.searchText = e.trim()
if (!e.trim()) {
emits('lastIdChange', 0, 0, 0)
}
zPaging.value?.reload()
}
// ES-
const queryAllSearch = (pageNum, searchResultPageSize) => {
let params = {
key: state.searchText, //
size: searchResultPageSize,
}
if (props.apiParams) {
let apiParams = JSON.parse(decodeURIComponent(props.apiParams))
params = Object.assign({}, params, apiParams)
}
const resp = props.apiRequest(params)
resp.then(({ code, data }) => {
console.log(data)
if (code == 200) {
if ((data.user_infos || []).length > 0) {
;(data.user_infos || []).forEach((item) => {
item.group_type = 0
})
}
if ((data.group_infos || []).length > 0) {
;(data.group_infos || []).forEach((item) => {
item.group_type = item.type
item.groupTempType = 'group_infos'
})
}
if ((data.group_member_infos || []).length > 0) {
;(data.group_member_infos || []).forEach((item) => {
item.groupTempType = 'group_member_infos'
})
}
if ((data.talk_record_infos || []).length > 0) {
state.first_talk_record_infos = Object.assign(
{},
state.first_talk_record_infos,
data.talk_record_infos[0],
)
;(data.talk_record_infos || []).forEach((item) => {
item.group_type = 0
})
}
let tempGeneral_infos = Array.isArray(data.general_infos)
? [...data.general_infos]
: data.general_infos
delete data.general_infos
data.combinedGroup = (data.group_infos || []).concat(
data.group_member_infos || [],
)
data.general_infos = tempGeneral_infos
let isEmpty = true
let dataKeys = Object.keys(data)
let paginationKey = ''
dataKeys.forEach((item) => {
if (Array.isArray(data[item]) && data[item].length > 0) {
paginationKey = item
isEmpty = false
}
})
if (isEmpty) {
if (pageNum == 1) {
zPaging.value?.complete([])
} else {
data = state.searchResult
zPaging.value?.complete([data])
}
} else {
if (props.isPagination) {
if (
paginationKey &&
Array.isArray(
(state?.searchResult && state?.searchResult[paginationKey]) || [],
) &&
((state?.searchResult && state?.searchResult[paginationKey]) || [])
.length > 0
) {
data[paginationKey] = state.searchResult[paginationKey].concat(
data[paginationKey],
)
}
emits(
'lastIdChange',
data.last_id,
data.last_group_id,
data.last_member_id,
)
let total = data.count
if (props.searchRecordDetail) {
total = data.group_record_count
}
zPaging.value?.completeByTotal([data], total)
} else {
zPaging.value?.complete([data])
}
}
state.searchResult = data
} else {
zPaging.value?.complete([])
}
})
resp.catch(() => {
zPaging.value?.complete([])
})
}
//
const cancelSearch = () => {
uni.navigateBack({
delta: 1,
})
}
//key
const getResultKeysValue = (keys) => {
let resultKey = ''
switch (keys) {
case 'user_infos':
resultKey = t('index.mine.addressBook')
break
case 'group_infos':
resultKey = t('chat.type.group')
break
case 'group_member_infos':
resultKey = t('chat.type.group')
break
case 'combinedGroup':
resultKey = t('chat.type.group')
break
case 'general_infos':
resultKey = t('chat.type.record')
break
case 'talk_record_infos':
resultKey = t('search.result.relevant') + t('chat.type.record')
break
default:
resultKey = ''
}
return resultKey
}
//
const getHasMoreResult = (searchResultKey) => {
let has_more_result = ''
switch (searchResultKey) {
case 'user_infos':
if (
state.searchResult['user_count'] &&
state.searchResult['user_count'] > 3
) {
has_more_result = t('has_more') + t('index.mine.addressBook')
}
break
case 'group_infos':
if (
state.searchResult['group_count'] &&
state.searchResult['group_count'] > 3
) {
has_more_result = t('has_more') + t('chat.type.group')
}
break
case 'group_member_infos':
if (
state.searchResult['group_count'] &&
state.searchResult['group_count'] > 3
) {
has_more_result = t('has_more') + t('chat.type.group')
}
break
case 'combinedGroup':
if (
state.searchResult['group_count'] &&
state.searchResult['group_count'] > 3
) {
has_more_result = t('has_more') + t('chat.type.group')
}
break
case 'general_infos':
break
default:
}
return has_more_result
}
//
const toMoreResultPage = (searchResultKey) => {
emits('toMoreResultPage', searchResultKey, state.searchText)
}
//
const clickSearchItem = (searchResultKey, searchItem) => {
emits(
'clickSearchItem',
state.searchText,
searchResultKey,
searchItem.talk_type,
searchItem.receiver_id,
)
}
</script>
<style lang="scss" scoped>
.search-list {
.searchRoot {
padding: 20rpx 48rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
.searchRoot_cancelBtn {
line-height: 44rpx;
color: $theme-primary;
margin: 0 0 0 20rpx;
flex-shrink: 0;
}
}
.search-record-detail {
padding: 0 50rpx;
}
.search-result {
width: 100%;
padding: 0 32rpx;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
.search-result-list {
width: 100%;
padding: 0 18rpx;
.search-result-part {
margin: 36rpx 0 0;
.result-title {
padding: 0 0 10rpx;
span {
line-height: 40rpx;
color: $theme-hint-text;
}
}
.result-has-more {
padding: 10px 0;
border-bottom: 1px solid $theme-border-color;
span {
color: #191919;
line-height: 40rpx;
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,50 @@
<template>
<div class="outer-layer search-page">
<div class="root">
<searchList
:searchResultPageSize="3"
:listLimit="true"
:apiRequest="ServeSeachQueryAll"
@toMoreResultPage="toMoreResultPage"
@clickSearchItem="clickSearchItem"
></searchList>
</div>
</div>
</template>
<script setup>
import searchList from './components/searchList.vue'
import { ServeSeachQueryAll } from '@/api/search/index'
//
const toMoreResultPage = (searchResultKey, searchText) => {
uni.navigateTo({
url:
'/pages/search/moreResult/moreResult?searchResultKey=' +
searchResultKey +
'&searchText=' +
searchText,
})
}
//
const clickSearchItem = (
searchText,
searchResultKey,
talk_type,
receiver_id,
) => {
console.log(searchResultKey)
if (searchResultKey === 'general_infos') {
uni.navigateTo({
url:
'/pages/search/moreResult/moreResultDetail?searchText=' +
searchText +
'&talk_type=' +
talk_type +
'&receiver_id=' +
receiver_id,
})
}
}
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,97 @@
<template>
<div class="outer-layer search-page">
<div class="root">
<searchList
:searchResultPageSize="10"
:listLimit="false"
:apiRequest="state.apiRequest"
:apiParams="state.apiParams"
:searchText="state.searchText"
:isPagination="true"
@lastIdChange="lastIdChange"
@clickSearchItem="clickSearchItem"
></searchList>
</div>
</div>
</template>
<script setup>
import searchList from '../components/searchList.vue'
import { onLoad } from '@dcloudio/uni-app'
import {
ServeQueryUser,
ServeQueryGroup,
ServeTalkRecord,
} from '@/api/search/index'
import { reactive } from 'vue'
const state = reactive({
apiRequest: Function,
apiParams: String,
searchText: String,
searchResultKey: String,
})
onLoad((options) => {
console.log(options)
if (options.searchResultKey) {
state.searchResultKey = options.searchResultKey
if (options.searchResultKey === 'user_infos') {
state.apiParams = encodeURIComponent(
JSON.stringify({
last_id: 0, //id
}),
)
state.apiRequest = ServeQueryUser
} else if (options.searchResultKey === 'combinedGroup') {
state.apiParams = encodeURIComponent(
JSON.stringify({
last_group_id: 0, //id
last_member_id: 0, //id
}),
)
state.apiRequest = ServeQueryGroup
} else if (options.searchResultKey === 'general_infos') {
state.apiParams = encodeURIComponent(
JSON.stringify({
talk_type: 0, //12
receiver_id: 0, //
last_group_id: 0, //id
last_member_id: 0, //id
}),
)
state.apiRequest = ServeTalkRecord
}
}
if (options.searchText) {
state.searchText = options.searchText
}
})
//id
const lastIdChange = (last_id, last_group_id, last_member_id) => {
let idChanges = {
last_id,
last_group_id,
last_member_id,
}
state.apiParams = encodeURIComponent(
JSON.stringify(
Object.assign(
{},
JSON.parse(decodeURIComponent(state.apiParams)),
idChanges,
),
),
)
}
//
const clickSearchItem = (searchText) => {
if (state.searchResultKey === 'general_infos') {
uni.navigateTo({
url: '/pages/search/moreResult/moreResultDetail?searchText=' + searchText,
})
}
}
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,71 @@
<template>
<div class="outer-layer search-page">
<div class="root">
<searchList
:searchResultPageSize="10"
:listLimit="false"
:apiRequest="ServeTalkRecord"
:apiParams="state.apiParams"
:searchText="state.searchText"
:isPagination="true"
:searchRecordDetail="true"
@lastIdChange="lastIdChange"
></searchList>
</div>
</div>
</template>
<script setup>
import searchList from '../components/searchList.vue'
import { onLoad } from '@dcloudio/uni-app'
import { ServeTalkRecord } from '@/api/search/index'
import { reactive } from 'vue'
const state = reactive({
apiParams: String,
searchText: String,
})
onLoad((options) => {
console.log(options)
let talk_type = 0
if (options.talk_type) {
talk_type = Number(options.talk_type)
}
let receiver_id = 0
if (options.receiver_id) {
receiver_id = Number(options.receiver_id)
}
state.apiParams = encodeURIComponent(
JSON.stringify({
talk_type: talk_type, //12
receiver_id: receiver_id, //
last_group_id: 0, //id
last_member_id: 0, //id
}),
)
if (options.searchText) {
state.searchText = options.searchText
}
console.log(JSON.parse(decodeURIComponent(state.apiParams)))
})
//id
const lastIdChange = (last_id, last_group_id, last_member_id) => {
let idChanges = {
last_id,
last_group_id,
last_member_id,
}
state.apiParams = encodeURIComponent(
JSON.stringify(
Object.assign(
{},
JSON.parse(decodeURIComponent(state.apiParams)),
idChanges,
),
),
)
}
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,241 @@
<template>
<div class="outer-layer search-by-condition-page">
<div class="root">
<ZPaging ref="zPaging" :show-scrollbar="false">
<template #top>
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
<div class="navBar-title flex flex-col items-center justify-center">
<span class="text-[34rpx] font-medium">
{{ state.pageTitle }}
</span>
</div>
</tm-navbar>
</template>
<div v-if="state.condition === 'date'" class="search-by-date">
<tm-time-picker
:show="state.showMonthPicker"
:showDetail="{
year: true,
month: true,
day: false,
hour: false,
minute: false,
second: false,
am_pm: false,
}"
:showSuffix="{
year: '',
month: '',
}"
:defaultValue="state.nowDate"
format="YYYY年MM月"
start=""
:end="state.maxDate"
@confirm="confirmSelectedMonth"
:round="0"
:title="$t('search.condition.date_pickerTitle')"
>
<div class="search-date-picker">
<span class="text-[28rpx] font-regular">
{{ state.selectedMonth }}
</span>
<img src="/src/static/image/search/down-pointer.png" />
</div>
</tm-time-picker>
<tm-calendar-view
:show="true"
:hideTool="true"
:hideButton="true"
:dateStyle="state.dateStyle"
:defaultValue="state.selectedDateArray"
v-model="state.selectedDateArray"
:disabledDate="state.disabledDateArray"
@click="selectDate"
model="day"
:end="state.maxDate"
@getDArray="getDArray"
:showDefault="false"
></tm-calendar-view>
</div>
</ZPaging>
</div>
</div>
</template>
<script setup>
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import searchList from '../components/searchList.vue'
import { parseTime } from '@/utils/datetime'
import { onMounted, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { ServeTalkDate } from '@/api/search/index'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
let nowDay = new Date().setHours(0, 0, 0, 0)
const state = reactive({
receiver_id: '', //id
pageTitle: '', //
dateStyle: [], //
nowDate: new Date(nowDay), //
maxDate: new Date(nowDay), //
selectedDateArray: Array(new Date(nowDay)), //
showMonthPicker: false, //
selectedMonth: new Date(nowDay), //
disabledDateArray: [], //
dArray: [], //
})
onLoad((options) => {
console.log(options)
if (options.receiver_id) {
state.receiver_id = Number(options.receiver_id)
}
if (options.condition) {
state.condition = options.condition
if (options.condition === 'date') {
state.pageTitle = t('search.condition.date')
ServeQueryTalkDate(parseTime(state.nowDate, '{y}{m}'))
}
}
})
onMounted(() => {
state.selectedMonth = parseTime(state.selectedMonth, '{y}年{m}月')
state.dateStyle = [
{
date: state.nowDate, //
text: false, //
color: '#46299D', //.
extra: '今天', //
},
]
})
//
const ServeQueryTalkDate = (month) => {
let params = {
month: month,
talk_type: 2, //12
receiver_id: state.receiver_id, //id
}
const resp = ServeTalkDate(params)
console.log(resp)
resp.then(({ code, data }) => {
console.log(data)
if (code == 200) {
if (data && data.length > 0) {
const formattedData = data.map(
(item) =>
item.substring(0, 4) +
'/' +
item.substring(4, 6) +
'/' +
item.substring(6, 8),
)
let disabledDateArray = state.dArray.filter(
(dIt) => !formattedData.includes(dIt),
)
disabledDateArray = disabledDateArray.map((item) =>
item.replace(/\//g, '-'),
)
console.log(disabledDateArray)
state.disabledDateArray = disabledDateArray
} else {
state.disabledDateArray = state.dArray
}
} else {
}
})
resp.catch(() => {})
}
//
const selectDate = (e) => {
if (e == parseTime(state.nowDate, '{y}/{m}/{d}')) {
console.log('==今日')
state.dateStyle = [
{
date: state.nowDate, //
text: false, //
color: '#46299D', //.
extra: '今天', //
},
]
} else {
state.dateStyle = [
{
date: state.nowDate, //
text: false, //
color: '', //.
extra: '今天', //
},
{
date: new Date(e), //
text: false, //
color: '#46299D', //.
},
]
}
}
//
const confirmSelectedMonth = (e) => {
console.log(e)
state.selectedMonth = parseTime(e, '{y}年{m}月')
// console.log()
let newDate = new Date(e)
newDate.setHours(0, 0, 0, 0)
newDate.setDate(1)
state.selectedDateArray = Array(new Date(newDate))
state.dateStyle = [
{
date: state.nowDate, //
text: false, //
color: '', //.
extra: '今天', //
},
]
ServeQueryTalkDate(parseTime(e, '{y}{m}'))
}
//
const getDArray = (dArray) => {
state.dArray = dArray
}
</script>
<style scoped lang="scss">
.search-by-date {
.search-date-picker {
padding: 20rpx 32rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
span {
line-height: 40rpx;
color: $theme-hint-text;
}
img {
width: 18rpx;
height: 11rpx;
margin: 0 0 0 26rpx;
}
}
}
body::v-deep .text-overflow-1 {
color: #666666 !important;
line-height: 44rpx !important;
font-size: 32rpx !important;
font-weight: bold !important;
}
body::v-deep .tmicon-times-circle-fill {
width: 37rpx;
height: 37rpx;
}
body::v-deep .round-3 {
background: linear-gradient(to right, #674bbc, #46299d);
}
</style>

View File

@ -1 +1,4 @@
$theme-primary: #46299D; $theme-primary: #46299d;
$theme-text: #191919;
$theme-hint-text: #999999;
$theme-border-color: #f8f8f8;

3
src/static/css/font.scss Normal file
View File

@ -0,0 +1,3 @@
.font-regular {
font-weight: 400;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -7,3 +7,4 @@ export * from '@/store/modules/editor-draft'
export * from '@/store/modules/uploads' export * from '@/store/modules/uploads'
export * from '@/store/modules/dialogueList' export * from '@/store/modules/dialogueList'
// export * from '@/store/modules/note' // export * from '@/store/modules/note'
export * from '@/store/modules/group'

View File

@ -3,7 +3,7 @@ import {
ServeRemoveRecords, ServeRemoveRecords,
ServeRevokeRecords, ServeRevokeRecords,
ServePublishMessage, ServePublishMessage,
ServeCollectEmoticon ServeCollectEmoticon,
} from '@/api/chat/index' } from '@/api/chat/index'
import { ServeGetGroupMembers } from '@/api/group/index' import { ServeGetGroupMembers } from '@/api/group/index'
import { useEditorStore } from './editor' import { useEditorStore } from './editor'
@ -22,7 +22,7 @@ export const useDialogueStore = defineStore('dialogue', {
talk: { talk: {
username: '', username: '',
talk_type: 0, // 对话来源[1:私聊;2:群聊] talk_type: 0, // 对话来源[1:私聊;2:群聊]
receiver_id: 0 receiver_id: 0,
}, },
// 好友是否正在输入文字 // 好友是否正在输入文字
@ -55,20 +55,26 @@ export const useDialogueStore = defineStore('dialogue', {
talk_type: 1, // 对话类型 talk_type: 1, // 对话类型
receiver_id: 0, // 接收者ID receiver_id: 0, // 接收者ID
read_sequence: 0, // 当前已读的最后一条记录 read_sequence: 0, // 当前已读的最后一条记录
records: [] records: [],
} },
}, },
// 转发消息类型 // 转发消息类型
forwardType: 1, forwardType: 1,
// 合并转发消息 // 合并转发消息
forwardMessages: [] forwardMessages: [],
} }
}, },
getters: { getters: {
// 多选列表 // 多选列表
selectItems: (state) => state.records.filter((item) => item.isCheck), selectItems: (state) => state.records.filter((item) => item.isCheck),
// 当前对话是否是群聊 // 当前对话是否是群聊
isGroupTalk: (state) => state.talk.talk_type === 2 isGroupTalk: (state) => state.talk.talk_type === 2,
//获取被禁言的成员列表
getSilenceMember: (state) =>
state.members.filter((item) => item.is_mute === 1),
//获取群管理员
getAdminList: (state) =>
state.members.filter((item) => item.leader === 1),
}, },
actions: { actions: {
// 更新在线状态 // 更新在线状态
@ -82,7 +88,7 @@ export const useDialogueStore = defineStore('dialogue', {
this.talk = { this.talk = {
username: data.remark || data.name, username: data.remark || data.name,
talk_type: data.talk_type, talk_type: data.talk_type,
receiver_id: data.receiver_id receiver_id: data.receiver_id,
} }
this.index_name = `${data.talk_type}_${data.receiver_id}` this.index_name = `${data.talk_type}_${data.receiver_id}`
@ -99,7 +105,7 @@ export const useDialogueStore = defineStore('dialogue', {
// 更新提及列表 // 更新提及列表
async updateGroupMembers() { async updateGroupMembers() {
let { code, data } = await ServeGetGroupMembers({ let { code, data } = await ServeGetGroupMembers({
group_id: this.talk.receiver_id group_id: this.talk.receiver_id,
}) })
if (code != 200) return if (code != 200) return
@ -112,7 +118,8 @@ export const useDialogueStore = defineStore('dialogue', {
leader: o.leader, leader: o.leader,
remark: o.remark, remark: o.remark,
online: false, online: false,
value: o.nickname value: o.nickname,
key: o.key
})) }))
}, },
@ -191,7 +198,7 @@ export const useDialogueStore = defineStore('dialogue', {
ServeRemoveRecords({ ServeRemoveRecords({
talk_type: this.talk.talk_type, talk_type: this.talk.talk_type,
receiver_id: this.talk.receiver_id, receiver_id: this.talk.receiver_id,
msg_ids: msgIds msg_ids: msgIds,
}).then((res) => { }).then((res) => {
if (res.code == 200) { if (res.code == 200) {
this.batchDelDialogueRecord(msgIds) this.batchDelDialogueRecord(msgIds)
@ -219,9 +226,9 @@ export const useDialogueStore = defineStore('dialogue', {
type: 'forward', type: 'forward',
receiver: { receiver: {
talk_type: this.talk.talk_type, talk_type: this.talk.talk_type,
receiver_id: this.talk.receiver_id receiver_id: this.talk.receiver_id,
}, },
...options ...options,
} }
ServePublishMessage(data).then((res) => { ServePublishMessage(data).then((res) => {
@ -247,6 +254,6 @@ export const useDialogueStore = defineStore('dialogue', {
// 设置合并转发消息 // 设置合并转发消息
setForwardMessages(messages) { setForwardMessages(messages) {
this.forwardMessages = messages this.forwardMessages = messages
} },
} },
}) })

View File

@ -0,0 +1,47 @@
import { defineStore } from 'pinia'
import {
ServeGroupDetail,
ServeGetGroupMembers,
ServeGetGroupNotices,
} from '@/api/group/index'
import { useDialogueStore } from '@/store'
export const useGroupStore = defineStore('group', {
state: () => {
return {
groupInfo: '', //群聊信息
memberList: [], //群成员列表
groupNotice: [], //群公告
}
},
getters: {
//获取群聊信息
getGroupInfo: (state) => state.groupInfo,
//获取群公告
getGroupNotice: (state) => state.groupNotice,
},
actions: {
//获取群聊信息
async ServeGroupDetail() {
const dialogueStore = useDialogueStore()
let { code, data } = await ServeGroupDetail({
group_id: dialogueStore.talk.receiver_id,
})
if (code == 200) {
console.log(data)
this.groupInfo = data
}
},
//群公告查询
async ServeGetGroupNotices() {
const dialogueStore = useDialogueStore()
let { code, data } = await ServeGetGroupNotices({
group_id: dialogueStore.talk.receiver_id,
})
if (code == 200) {
this.groupNotice = data.items
} else {
}
},
},
})

View File

@ -18,6 +18,10 @@ export const useTalkStore = defineStore('talk', {
topItems: (state) => { topItems: (state) => {
return state.items.filter((item) => item.is_top == 1) return state.items.filter((item) => item.is_top == 1)
}, },
// 过滤所有免打扰对话列表
disturbItems: (state) => {
return state.items.filter((item) => item.is_disturb == 1)
},
// 对话列表 // 对话列表
talkItems: (state) => { talkItems: (state) => {

View File

@ -1,82 +1,149 @@
<template> <template>
<view class="flex flex-col relative"> <view class="flex flex-col relative">
<tm-sheet v-if="!props.hideTool" :shadow="0" :round="0" :height="88" :margin="[0, 0]" :padding="[0, 0]" _class="flex flex-col"> <tm-sheet
<view class="flex flex-row flex-row-center-center" style="height: 88rpx"> v-if="!props.hideTool"
<view @click.stop="prevYear" class="px-16"> :shadow="0"
<tm-icon :userInteractionEnabled="false" :font-size="22" name="tmicon-angle-double-left"></tm-icon> :round="0"
</view> :height="88"
<view @click.stop="prevMonth" class="px-16"> :margin="[0, 0]"
<tm-icon :userInteractionEnabled="false" :font-size="22" name="tmicon-angle-left"></tm-icon> :padding="[0, 0]"
</view> _class="flex flex-col"
<view class="px-12"> >
<tm-text :userInteractionEnabled="false" _class="text-weight-b" :font-size="32" :label="_nowDate"></tm-text> <view class="flex flex-row flex-row-center-center" style="height: 88rpx;">
</view> <view @click.stop="prevYear" class="px-16">
<view @click.stop="nextMonth" class="px-16"> <tm-icon
<tm-icon :userInteractionEnabled="false" :font-size="22" name="tmicon-angle-right"></tm-icon> :userInteractionEnabled="false"
</view> :font-size="22"
<view @click.stop="nextYear" class="px-16"> name="tmicon-angle-double-left"
<tm-icon :userInteractionEnabled="false" :font-size="22" name="tmicon-angle-double-right"></tm-icon> ></tm-icon>
</view> </view>
</view> <view @click.stop="prevMonth" class="px-16">
<view <tm-icon
@click="nowWeekClick" :userInteractionEnabled="false"
class="absolute t-0 r--6 zIndex-10 round-12 py-4 flex flex-row flex-row-center-center" :font-size="22"
style="width: 90rpx; height: 88rpx" name="tmicon-angle-left"
> ></tm-icon>
<tm-text :userInteractionEnabled="false" color="grey" _class="text-align-center" :font-size="28" :label="props.textUnit[8]"></tm-text> </view>
</view> <view class="px-12">
</tm-sheet> <tm-text
<view class="flex flex-row flex-row-center-center py-12" :style="[{ height: '74rpx' }]"> :userInteractionEnabled="false"
<view class="flex-1 flex-center" v-for="(item, index) in weekStr" :key="index"> _class="text-weight-b"
<view style="width: 62rpx" class="flex-center flex-col"> :font-size="32"
<tm-text :font-size="24" :label="item"></tm-text> :label="_nowDate"
</view> ></tm-text>
</view> </view>
</view> <view @click.stop="nextMonth" class="px-16">
<view class="flex flex-col"> <tm-icon
<view class="flex flex-row flex-row-center-center" :style="[{ height: '94rpx' }]" v-for="(item, index) in _data" :key="index"> :userInteractionEnabled="false"
<view @click="clickWeek(item2)" class="flex-1 flex flex-row flex-row-center-center" v-for="(item2, index2) in item" :key="index2"> :font-size="22"
<tm-sheet name="tmicon-angle-right"
:userInteractionEnabled="false" ></tm-icon>
:height="90" </view>
:width="90" <view @click.stop="nextYear" class="px-16">
:shadow="0" <tm-icon
:round="24" :userInteractionEnabled="false"
:border="item2.extra.color && isSelected(item2.dateStr) ? 1 : 0" :font-size="22"
_class="flex-row" name="tmicon-angle-double-right"
:transprent="item2.extra.color || !isSelected(item2.dateStr)" ></tm-icon>
:color="item2.extra.color ? item2.extra.color : isSelected(item2.dateStr) ? _color : 'white'" </view>
:margin="[0, 0]" </view>
:padding="[0, 0]" <view
> @click="nowWeekClick"
<view class="absolute t-0 r--6 zIndex-10 round-12 py-4 flex flex-row flex-row-center-center"
:userInteractionEnabled="false" style="width: 90rpx; height: 88rpx;"
style="width: 62rpx" >
:class="[!item2.isNowIn ? 'opacity-6' : '']" <tm-text
class="flex-1 flex-center" :userInteractionEnabled="false"
> color="grey"
<view style="width: 62rpx" class="flex-center flex-col" :style="[{ opacity: item2.disabled ? '0.3' : '1' }]"> _class="text-align-center"
<tm-text :font-size="28" :label="item2.date"></tm-text> :font-size="28"
<tm-text _class="flex-center" v-if="item2.extra.extra" :font-size="22" :label="item2.extra.extra"></tm-text> :label="props.textUnit[8]"
</view> ></tm-text>
</view> </view>
</tm-sheet> </tm-sheet>
</view> <view
</view> class="flex flex-row flex-row-center-center py-12"
</view> :style="[{ height: '74rpx' }]"
>
<view
class="flex-1 flex-center"
v-for="(item, index) in weekStr"
:key="index"
>
<view style="width: 62rpx;" class="flex-center flex-col">
<tm-text :font-size="24" :label="item"></tm-text>
</view>
</view>
</view>
<view class="flex flex-col">
<view
class="flex flex-row flex-row-center-center"
:style="[{ height: '94rpx' }]"
v-for="(item, index) in _data"
:key="index"
>
<view
@click="clickWeek(item2)"
class="flex-1 flex flex-row flex-row-center-center"
v-for="(item2, index2) in item"
:key="index2"
>
<tm-sheet
:userInteractionEnabled="false"
:height="90"
:width="90"
:shadow="0"
:round="24"
:border="item2.extra.color && isSelected(item2.dateStr) ? 1 : 0"
_class="flex-row"
:transprent="item2.extra.color || !isSelected(item2.dateStr)"
:color="
item2.extra.color
? item2.extra.color
: isSelected(item2.dateStr)
? _color
: 'white'
"
:margin="[0, 0]"
:padding="[0, 0]"
>
<view
:userInteractionEnabled="false"
style="width: 62rpx;"
:class="[!item2.isNowIn ? 'opacity-6' : '']"
class="flex-1 flex-center"
>
<view
style="width: 62rpx;"
class="flex-center flex-col"
:style="[{ opacity: item2.disabled ? '0.3' : '1' }]"
>
<tm-text :font-size="28" :label="item2.date"></tm-text>
<tm-text
_class="flex-center"
v-if="item2.extra.extra"
:font-size="22"
:label="item2.extra.extra"
></tm-text>
</view>
</view>
</tm-sheet>
</view>
</view>
</view>
<tm-button <tm-button
:followTheme="props.followTheme" :followTheme="props.followTheme"
v-if="!props.hideButton" v-if="!props.hideButton"
:linear="props.linear" :linear="props.linear"
:linear-deep="props.linearDeep" :linear-deep="props.linearDeep"
:color="props.color" :color="props.color"
@click="confirm" @click="confirm"
block block
:label="_confirmText" :label="_confirmText"
:margin="[0, 16]" :margin="[0, 16]"
></tm-button> ></tm-button>
</view> </view>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -103,88 +170,116 @@ const store = useTmpiniaStore()
* click-day 日期被选中时触发注意禁用的日期不会触发 * click-day 日期被选中时触发注意禁用的日期不会触发
* change 当切换年或者月的时候触发 * change 当切换年或者月的时候触发
*/ */
const emits = defineEmits(['update:modelValue', 'confirm', 'click-day', 'change']) const emits = defineEmits([
'update:modelValue',
'confirm',
'click-day',
'change',
'getDArray',
])
const props = defineProps({ const props = defineProps({
followTheme: { followTheme: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
// //
defaultValue: { defaultValue: {
type: Array as PropType<Array<String | Number | Date>>, type: Array as PropType<Array<String | Number | Date>>,
default: () => [] default: () => [],
}, },
// //
modelValue: { modelValue: {
type: Array as PropType<Array<String | Number | Date>>, type: Array as PropType<Array<String | Number | Date>>,
default: () => [] default: () => [],
}, },
color: { color: {
type: String, type: String,
default: 'primary' default: 'primary',
}, },
linear: { linear: {
type: String, type: String,
default: '' default: '',
}, },
linearDeep: { linearDeep: {
type: String, type: String,
default: 'light' default: 'light',
}, },
// //
start: { start: {
type: [String, Number, Date], type: [String, Number, Date],
default: '' default: '',
}, },
// //
end: { end: {
type: [String, Number, Date], type: [String, Number, Date],
default: '' default: '',
}, },
//["2022-1-1","2022-5-1"] //["2022-1-1","2022-5-1"]
// //
disabledDate: { disabledDate: {
type: Array as PropType<Array<String | Number | Date>>, type: Array as PropType<Array<String | Number | Date>>,
default: () => [] default: () => [],
}, },
// //
multiple: { multiple: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
// //
dateStyle: { dateStyle: {
type: Array as PropType<Array<dateItemStyle>>, type: Array as PropType<Array<dateItemStyle>>,
default: () => [] default: () => [],
}, },
//multiple=true //multiple=true
max: { max: {
type: Number, type: Number,
default: 999 default: 999,
}, },
// //
hideTool: { hideTool: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
// //
hideButton: { hideButton: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
confirmText: { confirmText: {
type: String, type: String,
default: '确认' default: '确认',
}, },
//便 //便
textUnit: { textUnit: {
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
default: ['周次','一','二','三','四','五','六','日','本日','本周','本月','本季度','本年','月','第${x}季度','年'] default: [
} '周次',
'一',
'二',
'三',
'四',
'五',
'六',
'日',
'本日',
'本周',
'本月',
'本季度',
'本年',
'月',
'第${x}季度',
'年',
],
},
showDefault: {
//
type: Boolean,
default: true,
},
}) })
const _color = computed(() => { const _color = computed(() => {
if (props.followTheme && store.tmStore.color) return store.tmStore.color if (props.followTheme && store.tmStore.color) return store.tmStore.color
return props.color return props.color
}) })
const _confirmText = computed(() => props.confirmText) const _confirmText = computed(() => props.confirmText)
@ -194,7 +289,7 @@ DayJs.extend(isSameOrBefore)
DayJs.extend(isBetween) DayJs.extend(isBetween)
// //
const _value = ref(props.defaultValue) const _value = ref(props.defaultValue)
const weekStr = props.textUnit.slice(1,8) const weekStr = props.textUnit.slice(1, 8)
// //
const showOpenDate: Ref<dayjs.Dayjs> = ref(setShowopenDate()) const showOpenDate: Ref<dayjs.Dayjs> = ref(setShowopenDate())
@ -202,233 +297,251 @@ const showOpenDate: Ref<dayjs.Dayjs> = ref(setShowopenDate())
const _data: Ref<Array<Array<monthDayItem>>> = ref([]) const _data: Ref<Array<Array<monthDayItem>>> = ref([])
// //
const _start_date = computed(() => { const _start_date = computed(() => {
let isv = DayJs(props.start).isValid() let isv = DayJs(props.start).isValid()
return isv ? DayJs(props.start) : DayJs('1980-1-1') return isv ? DayJs(props.start) : DayJs('1980-1-1')
}) })
// //
const _end_date = computed(() => { const _end_date = computed(() => {
let isv = DayJs(props.end).isValid() let isv = DayJs(props.end).isValid()
return isv ? DayJs(props.end) : DayJs('2450-1-1') return isv ? DayJs(props.end) : DayJs('2450-1-1')
}) })
// //
const _nowDate = computed(() => { const _nowDate = computed(() => {
return showOpenDate.value.format('YYYY-MM') return showOpenDate.value.format('YYYY-MM')
}) })
_data.value = getWeekOfMonthArray() _data.value = getWeekOfMonthArray()
watch( watch(
[() => props.modelValue, () => props.dateStyle, () => props.disabledDate, () => props.start, () => props.end], [
() => { () => props.modelValue,
_value.value = props.modelValue () => props.dateStyle,
showOpenDate.value = setShowopenDate() () => props.disabledDate,
_data.value = getWeekOfMonthArray() () => props.start,
}, () => props.end,
{ deep: true } ],
() => {
_value.value = props.modelValue
showOpenDate.value = setShowopenDate()
_data.value = getWeekOfMonthArray(true)
},
{ deep: true },
) )
// //
function setShowopenDate() { function setShowopenDate() {
// //
if (_value.value.length == 0) { if (_value.value.length == 0) {
return DayJs() return DayJs()
} }
let n = _value.value[0] || DayJs() let n = _value.value[0] || DayJs()
n = typeof n == 'undefined' || n == null ? DayJs() : n n = typeof n == 'undefined' || n == null ? DayJs() : n
return DayJs(n) return DayJs(n)
} }
function nowWeekClick() { function nowWeekClick() {
if (isDisabledDate(DayJs())) { if (isDisabledDate(DayJs())) {
uni.showToast({ title: '无法选中', icon: 'none' }) uni.showToast({ title: '无法选中', icon: 'none' })
return return
} }
selected(DayJs().format('YYYY/MM/DD')) selected(DayJs().format('YYYY/MM/DD'))
showOpenDate.value = DayJs() showOpenDate.value = DayJs()
_data.value = getWeekOfMonthArray() _data.value = getWeekOfMonthArray()
emits('click-day', DayJs().format('YYYY/MM/DD')) emits('click-day', DayJs().format('YYYY/MM/DD'))
} }
function clickWeek(wk: monthDayItem) { function clickWeek(wk: monthDayItem) {
if (wk.disabled) { if (wk.disabled) {
uni.showToast({ title: '无法选中', icon: 'none' }) if (props.showDefault) {
return uni.showToast({ title: '无法选中', icon: 'none' })
} }
selected(wk.dateStr) return
emits('click-day', wk.dateStr) }
selected(wk.dateStr)
emits('click-day', wk.dateStr)
} }
// //
function selected(item: string | dayjs.Dayjs | number) { function selected(item: string | dayjs.Dayjs | number) {
let fr = _value.value.filter((el) => DayJs(el).isSame(item)) let fr = _value.value.filter((el) => DayJs(el).isSame(item))
if (!props.multiple) { if (!props.multiple) {
_value.value = [DayJs(item).format('YYYY/MM/DD')] _value.value = [DayJs(item).format('YYYY/MM/DD')]
return return
} }
if (fr.length > 0) { if (fr.length > 0) {
_value.value = _value.value.filter((el) => !DayJs(el).isSame(item)) _value.value = _value.value.filter((el) => !DayJs(el).isSame(item))
} else { } else {
if (_value.value.length >= props.max) { if (_value.value.length >= props.max) {
uni.showToast({ title: '只可选择' + props.max + '天', icon: 'none' }) uni.showToast({ title: '只可选择' + props.max + '天', icon: 'none' })
return return
} }
_value.value.push(DayJs(item).format('YYYY/MM/DD')) _value.value.push(DayJs(item).format('YYYY/MM/DD'))
} }
} }
function nextYear() { function nextYear() {
showOpenDate.value = showOpenDate.value.add(1, 'year') showOpenDate.value = showOpenDate.value.add(1, 'year')
let dys = getWeekOfMonthArray() let dys = getWeekOfMonthArray()
emits('change', showOpenDate.value.format('YYYY/MM/DD')) emits('change', showOpenDate.value.format('YYYY/MM/DD'))
nextTick(() => { nextTick(() => {
_data.value = [...dys] _data.value = [...dys]
}) })
} }
function nextMonth() { function nextMonth() {
showOpenDate.value = showOpenDate.value.add(1, 'month') showOpenDate.value = showOpenDate.value.add(1, 'month')
let dys = getWeekOfMonthArray() let dys = getWeekOfMonthArray()
emits('change', showOpenDate.value.format('YYYY/MM/DD')) emits('change', showOpenDate.value.format('YYYY/MM/DD'))
nextTick(() => { nextTick(() => {
_data.value = [...dys] _data.value = [...dys]
}) })
} }
function prevMonth() { function prevMonth() {
showOpenDate.value = showOpenDate.value.subtract(1, 'month') showOpenDate.value = showOpenDate.value.subtract(1, 'month')
let dys = getWeekOfMonthArray() let dys = getWeekOfMonthArray()
emits('change', showOpenDate.value.format('YYYY/MM/DD')) emits('change', showOpenDate.value.format('YYYY/MM/DD'))
nextTick(() => { nextTick(() => {
_data.value = [...dys] _data.value = [...dys]
}) })
} }
function prevYear() { function prevYear() {
showOpenDate.value = showOpenDate.value.subtract(1, 'year') showOpenDate.value = showOpenDate.value.subtract(1, 'year')
let dys = getWeekOfMonthArray() let dys = getWeekOfMonthArray()
emits('change', showOpenDate.value.format('YYYY/MM/DD')) emits('change', showOpenDate.value.format('YYYY/MM/DD'))
nextTick(() => { nextTick(() => {
_data.value = [...dys] _data.value = [...dys]
}) })
} }
// //
function setDefault(data: Array<String | Number | Date> = []) { function setDefault(data: Array<String | Number | Date> = []) {
_value.value = data.length > 0 ? data : props.modelValue _value.value = data.length > 0 ? data : props.modelValue
showOpenDate.value = setShowopenDate() showOpenDate.value = setShowopenDate()
_data.value = getWeekOfMonthArray() _data.value = getWeekOfMonthArray()
} }
// //
function getWeekOfMonthArray() { function getWeekOfMonthArray(needDarray = false) {
let nowMonth: dayjs.Dayjs = showOpenDate.value.date(1) let nowMonth: dayjs.Dayjs = showOpenDate.value.date(1)
let startStatickDay = nowMonth.startOf('month') let startStatickDay = nowMonth.startOf('month')
let endStatickDay = nowMonth.endOf('month') let endStatickDay = nowMonth.endOf('month')
/// ///
let nowMonthDayNum = nowMonth.daysInMonth() let nowMonthDayNum = nowMonth.daysInMonth()
//1-7 //1-7
let startOfday = startStatickDay.isoWeekday() - 1 let startOfday = startStatickDay.isoWeekday() - 1
startStatickDay = nowMonth.subtract(Math.abs(startOfday), 'day') startStatickDay = nowMonth.subtract(Math.abs(startOfday), 'day')
//1-7 //1-7
let endOfday = 7 - endStatickDay.isoWeekday() let endOfday = 7 - endStatickDay.isoWeekday()
if (endOfday > 0) { if (endOfday > 0) {
endStatickDay = nowMonth.date(nowMonthDayNum).add(Math.abs(endOfday), 'day') endStatickDay = nowMonth.date(nowMonthDayNum).add(Math.abs(endOfday), 'day')
} }
let startd = DayJs(startStatickDay) let startd = DayJs(startStatickDay)
let arOfmonth: Array<number> = [] // let arOfmonth: Array<number> = [] //
let ar: Array<monthDayItem> = [] let ar: Array<monthDayItem> = []
function setAr() { function setAr() {
let dy = props.dateStyle.map((el) => { let dy = props.dateStyle.map((el) => {
el.date = DayJs(el.date).format('YYYY/MM/DD') el.date = DayJs(el.date).format('YYYY/MM/DD')
return el return el
}) })
let dyObj = {} let dyObj = {}
dy.forEach((el) => { dy.forEach((el) => {
dyObj[el.date] = el dyObj[el.date] = el
}) })
let dySet = Object.keys(dyObj) let dySet = Object.keys(dyObj)
while (startd.isSameOrBefore(endStatickDay)) { while (startd.isSameOrBefore(endStatickDay)) {
let idate = startd.format('YYYY/MM/DD') let idate = startd.format('YYYY/MM/DD')
let ext = dySet.includes(idate) ? dyObj[idate] : {} let ext = dySet.includes(idate) ? dyObj[idate] : {}
ar.push({ ar.push({
dateStr: idate, dateStr: idate,
date: startd.date() < 10 ? '0' + startd.date() : startd.date(), date: startd.date() < 10 ? '0' + startd.date() : startd.date(),
day: startd.isoWeekday(), day: startd.isoWeekday(),
week: startd.isoWeek(), week: startd.isoWeek(),
isNowIn: isInNowMonth(nowMonth, startd), isNowIn: isInNowMonth(nowMonth, startd),
disabled: isDisabledDate(startd), disabled: isDisabledDate(startd),
extra: { extra: {
date: idate, date: idate,
text: false, text: false,
color: '', color: '',
extra: '', extra: '',
...ext ...ext,
} },
}) })
arOfmonth.push(startd.isoWeek()) arOfmonth.push(startd.isoWeek())
startd = startd.add(1, 'day') startd = startd.add(1, 'day')
} }
} }
setAr() setAr()
// 6*7 42 // 6*7 42
if (ar.length < 42) { if (ar.length < 42) {
let chaJi = 42 - ar.length let chaJi = 42 - ar.length
endStatickDay = endStatickDay.add(chaJi, 'day') endStatickDay = endStatickDay.add(chaJi, 'day')
setAr() setAr()
} }
arOfmonth = [...new Set(arOfmonth)] arOfmonth = [...new Set(arOfmonth)]
let dArray: Array<Array<monthDayItem>> = [] let dArray: Array<Array<monthDayItem>> = []
let index = 0 let index = 0
dArray.push([]) dArray.push([])
ar.forEach((el) => { ar.forEach((el) => {
if (el.week == arOfmonth[index]) { if (el.week == arOfmonth[index]) {
dArray[index].push(el) dArray[index].push(el)
} else { } else {
index += 1 index += 1
dArray.push([]) dArray.push([])
dArray[index].push(el) dArray[index].push(el)
} }
}) })
return dArray if (needDarray) {
let datesArr: Array<String> = []
ar.forEach((it) => {
datesArr.push(it.dateStr)
})
emits('getDArray', datesArr)
}
return dArray
} }
// now. // now.
function isInNowMonth(date: string | dayjs.Dayjs | number = '', now: string | dayjs.Dayjs | number = '') { function isInNowMonth(
let startStatickDay = DayJs(date).startOf('month').format('YYYY/MM/DD') date: string | dayjs.Dayjs | number = '',
let endStatickDay = DayJs(date).endOf('month').format('YYYY/MM/DD') now: string | dayjs.Dayjs | number = '',
return DayJs(now).isBetween(startStatickDay, endStatickDay, 'day', '[]') ) {
let startStatickDay = DayJs(date).startOf('month').format('YYYY/MM/DD')
let endStatickDay = DayJs(date).endOf('month').format('YYYY/MM/DD')
return DayJs(now).isBetween(startStatickDay, endStatickDay, 'day', '[]')
} }
// //
function isDisabledDate(date: string | dayjs.Dayjs | number = '') { function isDisabledDate(date: string | dayjs.Dayjs | number = '') {
let valdate = DayJs(date) let valdate = DayJs(date)
let isds = false let isds = false
isds = !valdate.isBetween(_start_date.value, _end_date.value, 'day', '[]') isds = !valdate.isBetween(_start_date.value, _end_date.value, 'day', '[]')
for (let i = 0; i < props.disabledDate.length; i++) { for (let i = 0; i < props.disabledDate.length; i++) {
let item = props.disabledDate[i] let item = props.disabledDate[i]
if (DayJs(item).isSame(valdate)) { if (DayJs(item).isSame(valdate)) {
isds = true isds = true
break break
} }
} }
return isds return isds
} }
// //
function isSelected(date: string | dayjs.Dayjs | number = '') { function isSelected(date: string | dayjs.Dayjs | number = '') {
let fr = _value.value.filter((el) => DayJs(el).isSame(date)) let fr = _value.value.filter((el) => DayJs(el).isSame(date))
return fr.length > 0 return fr.length > 0 && props.showDefault
} }
function confirm() { function confirm() {
let ar = _value.value.map((el) => DayJs(el).format('YYYY/MM/DD')) let ar = _value.value.map((el) => DayJs(el).format('YYYY/MM/DD'))
emits('update:modelValue', ar) emits('update:modelValue', ar)
emits('confirm', ar) emits('confirm', ar)
} }
// //
defineExpose({ defineExpose({
setDefault: setDefault, setDefault: setDefault,
nextYear: nextYear, nextYear: nextYear,
nextMonth: nextMonth, nextMonth: nextMonth,
prevYear: prevYear, prevYear: prevYear,
prevMonth: prevMonth prevMonth: prevMonth,
}) })
</script> </script>

View File

@ -1,137 +1,139 @@
<template> <template>
<tm-sheet :margin="[0, 0]" :padding="[0, 0]"> <tm-sheet :margin="[0, 0]" :padding="[0, 0]">
<!-- 按日选择的日期可单选多选 --> <!-- 按日选择的日期可单选多选 -->
<range-day <range-day
:confirmText="_confirmText" :confirmText="_confirmText"
:textUnit="_textUnit" :textUnit="_textUnit"
:hideButton="props.hideButton" :hideButton="props.hideButton"
:hideTool="props.hideTool" :hideTool="props.hideTool"
:followTheme="props.followTheme" :followTheme="props.followTheme"
ref="rDay" ref="rDay"
@confirm="confirm" @confirm="confirm"
@click-day="click" @click-day="click"
@change="change" @change="change"
@update:model-value="_value = $event" @update:model-value="_value = $event"
:model-value="_value" :model-value="_value"
:default-value="_value" :default-value="_value"
v-if="_modelType == 'rang'" v-if="_modelType == 'rang'"
:dateStyle="props.dateStyle" :dateStyle="props.dateStyle"
:disabledDate="props.disabledDate" :disabledDate="props.disabledDate"
:start="props.start" :start="props.start"
:end="props.end" :end="props.end"
:color="props.color" :color="props.color"
:linear="props.linear" :linear="props.linear"
:linearDeep="props.linearDeep" :linearDeep="props.linearDeep"
></range-day> ></range-day>
<month-day <month-day
:confirmText="_confirmText" :showDefault="props.showDefault"
:textUnit="_textUnit" @getDArray="getDArray"
:hideButton="props.hideButton" :confirmText="_confirmText"
:hideTool="props.hideTool" :textUnit="_textUnit"
:followTheme="props.followTheme" :hideButton="props.hideButton"
ref="Day" :hideTool="props.hideTool"
@confirm="confirm" :followTheme="props.followTheme"
@click-day="click" ref="Day"
@change="change" @confirm="confirm"
@update:model-value="_value = $event" @click-day="click"
:model-value="_value" @change="change"
:default-value="_value" @update:model-value="_value = $event"
v-if="_modelType == 'day'" :model-value="_value"
:dateStyle="props.dateStyle" :default-value="_value"
:disabledDate="props.disabledDate" v-if="_modelType == 'day'"
:max="props.max" :dateStyle="props.dateStyle"
:multiple="props.multiple" :disabledDate="props.disabledDate"
:start="props.start" :max="props.max"
:end="props.end" :multiple="props.multiple"
:color="props.color" :start="props.start"
:linear="props.linear" :end="props.end"
:linearDeep="props.linearDeep" :color="props.color"
></month-day> :linear="props.linear"
<!-- 按年选择 --> :linearDeep="props.linearDeep"
<year-du ></month-day>
:confirmText="_confirmText" <!-- 按年选择 -->
:textUnit="_textUnit" <year-du
:hideButton="props.hideButton" :confirmText="_confirmText"
:hideTool="props.hideTool" :textUnit="_textUnit"
:followTheme="props.followTheme" :hideButton="props.hideButton"
ref="Year" :hideTool="props.hideTool"
@confirm="confirm" :followTheme="props.followTheme"
@click-year="click" ref="Year"
@change="change" @confirm="confirm"
@update:model-value="_value = $event" @click-year="click"
:model-value="_value" @change="change"
:default-value="_value" @update:model-value="_value = $event"
v-if="_modelType == 'year'" :model-value="_value"
:start="props.start" :default-value="_value"
:end="props.end" v-if="_modelType == 'year'"
:color="props.color" :start="props.start"
:linear="props.linear" :end="props.end"
:linearDeep="props.linearDeep" :color="props.color"
></year-du> :linear="props.linear"
<!-- 按月选择 --> :linearDeep="props.linearDeep"
<month-year ></year-du>
:confirmText="_confirmText" <!-- 按月选择 -->
:textUnit="_textUnit" <month-year
:hideButton="props.hideButton" :confirmText="_confirmText"
:hideTool="props.hideTool" :textUnit="_textUnit"
:followTheme="props.followTheme" :hideButton="props.hideButton"
ref="Month" :hideTool="props.hideTool"
@confirm="confirm" :followTheme="props.followTheme"
@click-month="click" ref="Month"
@change="change" @confirm="confirm"
@update:model-value="_value = $event" @click-month="click"
:model-value="_value" @change="change"
:default-value="_value" @update:model-value="_value = $event"
v-if="_modelType == 'month'" :model-value="_value"
:start="props.start" :default-value="_value"
:end="props.end" v-if="_modelType == 'month'"
:color="props.color" :start="props.start"
:linear="props.linear" :end="props.end"
:linearDeep="props.linearDeep" :color="props.color"
></month-year> :linear="props.linear"
<!-- 按季度选择 --> :linearDeep="props.linearDeep"
<month-quarter ></month-year>
:confirmText="_confirmText" <!-- 按季度选择 -->
:textUnit="_textUnit" <month-quarter
:hideButton="props.hideButton" :confirmText="_confirmText"
:hideTool="props.hideTool" :textUnit="_textUnit"
:followTheme="props.followTheme" :hideButton="props.hideButton"
ref="Month" :hideTool="props.hideTool"
@confirm="confirm" :followTheme="props.followTheme"
@click-month="click" ref="Month"
@change="change" @confirm="confirm"
@update:model-value="_value = $event" @click-month="click"
:model-value="_value" @change="change"
:default-value="_value" @update:model-value="_value = $event"
v-if="_modelType == 'quarter'" :model-value="_value"
:start="props.start" :default-value="_value"
:end="props.end" v-if="_modelType == 'quarter'"
:color="props.color" :start="props.start"
:linear="props.linear" :end="props.end"
:linearDeep="props.linearDeep" :color="props.color"
></month-quarter> :linear="props.linear"
<!-- 按周选择时段 --> :linearDeep="props.linearDeep"
<week-day ></month-quarter>
:confirmText="_confirmText" <!-- 按周选择时段 -->
:textUnit="_textUnit" <week-day
:hideButton="props.hideButton" :confirmText="_confirmText"
:hideTool="props.hideTool" :textUnit="_textUnit"
:followTheme="props.followTheme" :hideButton="props.hideButton"
ref="Week" :hideTool="props.hideTool"
@confirm="confirm" :followTheme="props.followTheme"
@click-week="click" ref="Week"
@change="change" @confirm="confirm"
@update:model-value="_value = $event" @click-week="click"
:model-value="_value" @change="change"
:default-value="_value" @update:model-value="_value = $event"
v-if="_modelType == 'week'" :model-value="_value"
:start="props.start" :default-value="_value"
:end="props.end" v-if="_modelType == 'week'"
:color="props.color" :start="props.start"
:linear="props.linear" :end="props.end"
:linearDeep="props.linearDeep" :color="props.color"
></week-day> :linear="props.linear"
</tm-sheet> :linearDeep="props.linearDeep"
></week-day>
</tm-sheet>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -139,8 +141,23 @@
* 日历(嵌入页面面板) * 日历(嵌入页面面板)
* @description 可以按月按日按周按季度显示 * @description 可以按月按日按周按季度显示
*/ */
import { computed, ref, watch, PropType, Ref, getCurrentInstance, nextTick, watchEffect } from 'vue' import {
import { custom_props, computedTheme, computedClass, computedStyle, computedDark } from '../../tool/lib/minxs' computed,
ref,
watch,
PropType,
Ref,
getCurrentInstance,
nextTick,
watchEffect,
} from 'vue'
import {
custom_props,
computedTheme,
computedClass,
computedStyle,
computedDark,
} from '../../tool/lib/minxs'
import weekDay from './week-day.vue' import weekDay from './week-day.vue'
import monthYear from './month-year.vue' import monthYear from './month-year.vue'
import yearDu from './year-du.vue' import yearDu from './year-du.vue'
@ -149,7 +166,13 @@ import rangeDay from './range-day.vue'
import monthQuarter from './month-quarter.vue' import monthQuarter from './month-quarter.vue'
import tmSheet from '../tm-sheet/tm-sheet.vue' import tmSheet from '../tm-sheet/tm-sheet.vue'
import * as dayjs from '../../tool/dayjs/esm' import * as dayjs from '../../tool/dayjs/esm'
import { monthDayItem, dateItemStyle, monthYearItem, weekItem, yearItem } from './interface' import {
monthDayItem,
dateItemStyle,
monthYearItem,
weekItem,
yearItem,
} from './interface'
const proxy = getCurrentInstance()?.proxy ?? null const proxy = getCurrentInstance()?.proxy ?? null
const rDay = ref<InstanceType<typeof rangeDay> | null>(null) const rDay = ref<InstanceType<typeof rangeDay> | null>(null)
const Day = ref<InstanceType<typeof monthDay> | null>(null) const Day = ref<InstanceType<typeof monthDay> | null>(null)
@ -163,174 +186,209 @@ const Week = ref<InstanceType<typeof weekDay> | null>(null)
* click 日期被选中时触发注意禁用的日期不会触发 * click 日期被选中时触发注意禁用的日期不会触发
* change 当切换年或者月的时候触发 * change 当切换年或者月的时候触发
*/ */
const emits = defineEmits(['update:modelValue', 'update:modelStr', 'confirm', 'click', 'change']) const emits = defineEmits([
'update:modelValue',
'update:modelStr',
'confirm',
'click',
'change',
'getDArray',
])
const props = defineProps({ const props = defineProps({
...custom_props, ...custom_props,
followTheme: { followTheme: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
/** /**
* 数组 * 数组
*/ */
defaultValue: { defaultValue: {
type: Array as PropType<Array<String | Number | Date>>, type: Array as PropType<Array<String | Number | Date>>,
default: () => [] default: () => [],
}, },
modelValue: { modelValue: {
type: Array as PropType<Array<String | Number | Date>>, type: Array as PropType<Array<String | Number | Date>>,
default: () => [] default: () => [],
}, },
// //
//input便 //input便
//使modelValue //使modelValue
modelStr: { modelStr: {
type: String, type: String,
default: '' default: '',
}, },
/** /**
* 日期模式 * 日期模式
* day : 单个日期选择模式可多选需要设置multiple=true; * day : 单个日期选择模式可多选需要设置multiple=true;
* week :按周选择模式 * week :按周选择模式
* month :按月选择模式 * month :按月选择模式
* year :按年选择模式 * year :按年选择模式
* rang :按日期范围选择模式 * rang :按日期范围选择模式
* quarter :按季度选择模式 * quarter :按季度选择模式
*/ */
model: { model: {
type: String as PropType<'quarter' | 'day' | 'month' | 'year' | 'rang' | 'week'>, type: String as PropType<
default: 'day' 'quarter' | 'day' | 'month' | 'year' | 'rang' | 'week'
}, >,
color: { default: 'day',
type: String, },
default: 'primary' color: {
}, type: String,
linear: { default: 'primary',
type: String, },
default: '' linear: {
}, type: String,
linearDeep: { default: '',
type: String, },
default: 'light' linearDeep: {
}, type: String,
// default: 'light',
start: { },
type: [String, Number, Date], //
default: '' start: {
}, type: [String, Number, Date],
// default: '',
end: { },
type: [String, Number, Date], //
default: '' end: {
}, type: [String, Number, Date],
default: '',
},
/** 日历组件属性 */ /** 日历组件属性 */
//["2022-1-1","2022-5-1"] //["2022-1-1","2022-5-1"]
// //
disabledDate: { disabledDate: {
type: Array as PropType<Array<String | Number | Date>>, type: Array as PropType<Array<String | Number | Date>>,
default: () => [] default: () => [],
}, },
// //
multiple: { multiple: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
// //
dateStyle: { dateStyle: {
type: Array as PropType<Array<dateItemStyle>>, type: Array as PropType<Array<dateItemStyle>>,
default: () => [] default: () => [],
}, },
//multiple=true //multiple=true
max: { max: {
type: Number, type: Number,
default: 999 default: 999,
}, },
/** 日历组件属性结束 */ /** 日历组件属性结束 */
// //
hideButton: { hideButton: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
// //
hideTool: { hideTool: {
type: Boolean, type: Boolean,
default: false default: false,
}, },
/**modelStr的格式化输出选项不会影响value值只对输出值有效 */ /**modelStr的格式化输出选项不会影响value值只对输出值有效 */
format: { format: {
type: String, type: String,
default: 'YYYY/MM/DD' default: 'YYYY/MM/DD',
}, },
confirmText: { confirmText: {
type: String, type: String,
default: '确认' default: '确认',
}, },
//便 //便
textUnit: { textUnit: {
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
default: ['周次','一','二','三','四','五','六','日','本日','本周','本月','本季度','本年','月','第${x}季度','年'] default: [
} '周次',
'一',
'二',
'三',
'四',
'五',
'六',
'日',
'本日',
'本周',
'本月',
'本季度',
'本年',
'月',
'第${x}季度',
'年',
],
},
showDefault: {
//
type: Boolean,
default: true,
},
}) })
const _value = ref(props.defaultValue) const _value = ref(props.defaultValue)
const _modelType = computed(() => props.model) const _modelType = computed(() => props.model)
const _confirmText = computed(() => props.confirmText) const _confirmText = computed(() => props.confirmText)
const _textUnit = computed(() => props.textUnit) const _textUnit = computed(() => props.textUnit)
watch( watch(
() => props.modelValue, () => props.modelValue,
() => (_value.value = props.modelValue), () => (_value.value = props.modelValue),
{ deep: true } { deep: true },
) )
watch( watch(
_value, _value,
() => { () => {
let fmar = _value.value.map((el) => dayjs.default(el).format(props.format)) let fmar = _value.value.map((el) => dayjs.default(el).format(props.format))
let fm = fmar.join('~') let fm = fmar.join('~')
emits('update:modelStr', fm) emits('update:modelStr', fm)
}, },
{ deep: true } { deep: true },
) )
function change(e: Array<string | number>) { function change(e: Array<string | number>) {
emits('change', e) emits('change', e)
} }
function click(e: Array<string | number>) { function click(e: Array<string | number>) {
emits('click', e) emits('click', e)
} }
function confirm(e: Array<string | number>) { function confirm(e: Array<string | number>) {
emits('confirm', e) emits('confirm', e)
emits('update:modelValue', e) emits('update:modelValue', e)
} }
function getRefs() { function getRefs() {
if (_modelType.value == 'day') return Day.value if (_modelType.value == 'day') return Day.value
if (_modelType.value == 'rang') return rDay.value if (_modelType.value == 'rang') return rDay.value
if (_modelType.value == 'week') return Week.value if (_modelType.value == 'week') return Week.value
if (_modelType.value == 'month') return Month.value if (_modelType.value == 'month') return Month.value
if (_modelType.value == 'year') return Year.value if (_modelType.value == 'year') return Year.value
return Day.value return Day.value
} }
/** /**
* ref方法外部如果要即时调用 请注意包裹在nextTick中执行 * ref方法外部如果要即时调用 请注意包裹在nextTick中执行
*/ */
defineExpose({ defineExpose({
setDefault: (e: Event) => { setDefault: (e: Event) => {
nextTick(() => getRefs().setDefault(e)) nextTick(() => getRefs().setDefault(e))
}, },
nextYear: () => { nextYear: () => {
nextTick(() => getRefs().nextYear()) nextTick(() => getRefs().nextYear())
}, },
// mont,year // mont,year
nextMonth: () => { nextMonth: () => {
nextTick(() => getRefs().nextMonth()) nextTick(() => getRefs().nextMonth())
}, },
prevYear: () => { prevYear: () => {
nextTick(() => getRefs().prevYear()) nextTick(() => getRefs().prevYear())
}, },
// mont,year // mont,year
prevMonth: () => { prevMonth: () => {
nextTick(() => getRefs().prevMonth()) nextTick(() => getRefs().prevMonth())
} },
}) })
const getDArray = (dArray: Array<String>) => {
emits('getDArray', dArray)
}
</script> </script>

View File

@ -123,7 +123,8 @@ const props = defineProps({
day: true, day: true,
hour: false, hour: false,
minute: false, minute: false,
second: false second: false,
am_pm: true
} }
} }
}, },

View File

@ -5,6 +5,7 @@ export interface showDetail {
hour: boolean, hour: boolean,
minute: boolean, minute: boolean,
second: boolean, second: boolean,
am_pm: boolean
} }
export enum timeDetailType { export enum timeDetailType {
year = "year", year = "year",
@ -25,4 +26,4 @@ export interface timeArrayType {
hour: Array<number>, hour: Array<number>,
minute: Array<number>, minute: Array<number>,
second: Array<number>, second: Array<number>,
} }

View File

@ -1,11 +1,14 @@
<template> <template>
<view class="flex relative flex-col" :style="{ height: props.height + 'rpx' }"> <view
<view style="display: flex"> class="flex relative flex-col"
:style="{ height: props.height + 'rpx' }"
>
<view style="display: flex;">
<picker-view <picker-view
v-if="show" v-if="show"
:value="colIndex" :value="colIndex"
@change="colchange" @change="colchange"
:style="{ height: props.height + 'rpx',width:' 100%' }" :style="{ height: props.height + 'rpx', width: ' 100%' }"
:mask-style="maskStyle" :mask-style="maskStyle"
:immediateChange="props.immediateChange" :immediateChange="props.immediateChange"
indicator-style="height:50px" indicator-style="height:50px"
@ -16,7 +19,6 @@
:key="index" :key="index"
class="flex itemcel flex-row flex-row-center-center" class="flex itemcel flex-row flex-row-center-center"
:class="[colIndex[0] == index ? '' : 'UnitemSelected']" :class="[colIndex[0] == index ? '' : 'UnitemSelected']"
> >
<!-- #ifdef APP-NVUE --> <!-- #ifdef APP-NVUE -->
<TmText <TmText
@ -26,9 +28,10 @@
></TmText> ></TmText>
<!-- #endif --> <!-- #endif -->
<!-- #ifndef APP-NVUE --> <!-- #ifndef APP-NVUE -->
<text :style="{color:store.tmStore.dark?'white':'black'}">{{item + showSuffix['year']}}</text> <text :style="{ color: store.tmStore.dark ? 'white' : 'black' }">
{{ item + showSuffix['year'] }}
</text>
<!-- #endif --> <!-- #endif -->
</view> </view>
</picker-view-column> </picker-view-column>
<picker-view-column v-if="showCol.month"> <picker-view-column v-if="showCol.month">
@ -38,7 +41,6 @@
class="flex itemcel flex-row flex-row-center-center" class="flex itemcel flex-row flex-row-center-center"
:class="[colIndex[1] == index ? '' : 'UnitemSelected']" :class="[colIndex[1] == index ? '' : 'UnitemSelected']"
> >
<!-- #ifdef APP-NVUE --> <!-- #ifdef APP-NVUE -->
<TmText <TmText
:font-size="30" :font-size="30"
@ -47,9 +49,10 @@
></TmText> ></TmText>
<!-- #endif --> <!-- #endif -->
<!-- #ifndef APP-NVUE --> <!-- #ifndef APP-NVUE -->
<text :style="{color:store.tmStore.dark?'white':'black'}">{{item + 1 + showSuffix['month']}}</text> <text :style="{ color: store.tmStore.dark ? 'white' : 'black' }">
{{ item + 1 + showSuffix['month'] }}
</text>
<!-- #endif --> <!-- #endif -->
</view> </view>
</picker-view-column> </picker-view-column>
<picker-view-column v-if="showCol.day"> <picker-view-column v-if="showCol.day">
@ -59,7 +62,6 @@
class="flex itemcel flex-row flex-row-center-center" class="flex itemcel flex-row flex-row-center-center"
:class="[colIndex[2] == index ? '' : 'UnitemSelected']" :class="[colIndex[2] == index ? '' : 'UnitemSelected']"
> >
<!-- #ifdef APP-NVUE --> <!-- #ifdef APP-NVUE -->
<TmText <TmText
:font-size="30" :font-size="30"
@ -68,7 +70,9 @@
></TmText> ></TmText>
<!-- #endif --> <!-- #endif -->
<!-- #ifndef APP-NVUE --> <!-- #ifndef APP-NVUE -->
<text :style="{color:store.tmStore.dark?'white':'black'}">{{item + showSuffix['date']}}</text> <text :style="{ color: store.tmStore.dark ? 'white' : 'black' }">
{{ item + showSuffix['date'] }}
</text>
<!-- #endif --> <!-- #endif -->
</view> </view>
</picker-view-column> </picker-view-column>
@ -79,7 +83,6 @@
class="flex itemcel flex-row flex-row-center-center" class="flex itemcel flex-row flex-row-center-center"
:class="[colIndex[3] == index ? '' : 'UnitemSelected']" :class="[colIndex[3] == index ? '' : 'UnitemSelected']"
> >
<!-- #ifdef APP-NVUE --> <!-- #ifdef APP-NVUE -->
<TmText <TmText
:font-size="30" :font-size="30"
@ -88,7 +91,9 @@
></TmText> ></TmText>
<!-- #endif --> <!-- #endif -->
<!-- #ifndef APP-NVUE --> <!-- #ifndef APP-NVUE -->
<text :style="{color:store.tmStore.dark?'white':'black'}">{{item + showSuffix['hour']}}</text> <text :style="{ color: store.tmStore.dark ? 'white' : 'black' }">
{{ item + showSuffix['hour'] }}
</text>
<!-- #endif --> <!-- #endif -->
</view> </view>
</picker-view-column> </picker-view-column>
@ -99,7 +104,6 @@
class="flex itemcel flex-row flex-row-center-center" class="flex itemcel flex-row flex-row-center-center"
:class="[colIndex[4] == index ? '' : 'UnitemSelected']" :class="[colIndex[4] == index ? '' : 'UnitemSelected']"
> >
<!-- #ifdef APP-NVUE --> <!-- #ifdef APP-NVUE -->
<TmText <TmText
:font-size="30" :font-size="30"
@ -108,7 +112,9 @@
></TmText> ></TmText>
<!-- #endif --> <!-- #endif -->
<!-- #ifndef APP-NVUE --> <!-- #ifndef APP-NVUE -->
<text :style="{color:store.tmStore.dark?'white':'black'}">{{item + showSuffix['minute']}}</text> <text :style="{ color: store.tmStore.dark ? 'white' : 'black' }">
{{ item + showSuffix['minute'] }}
</text>
<!-- #endif --> <!-- #endif -->
</view> </view>
</picker-view-column> </picker-view-column>
@ -119,7 +125,6 @@
class="flex itemcel flex-row flex-row-center-center" class="flex itemcel flex-row flex-row-center-center"
:class="[colIndex[5] == index ? '' : 'UnitemSelected']" :class="[colIndex[5] == index ? '' : 'UnitemSelected']"
> >
<!-- #ifdef APP-NVUE --> <!-- #ifdef APP-NVUE -->
<TmText <TmText
:font-size="30" :font-size="30"
@ -128,7 +133,9 @@
></TmText> ></TmText>
<!-- #endif --> <!-- #endif -->
<!-- #ifndef APP-NVUE --> <!-- #ifndef APP-NVUE -->
<text :style="{color:store.tmStore.dark?'white':'black'}">{{item + showSuffix['second']}}</text> <text :style="{ color: store.tmStore.dark ? 'white' : 'black' }">
{{ item + showSuffix['second'] }}
</text>
<!-- #endif --> <!-- #endif -->
</view> </view>
</picker-view-column> </picker-view-column>
@ -136,19 +143,24 @@
<picker-view <picker-view
:value="ampmIndex" :value="ampmIndex"
@change="change1" @change="change1"
:style="{ height: props.height + 'rpx',width: `${100/(trueCount+1)}%` }" :style="{
height: props.height + 'rpx',
width: `${100 / (trueCount + 1)}%`,
}"
:mask-style="maskStyle" :mask-style="maskStyle"
:immediateChange="props.immediateChange" :immediateChange="props.immediateChange"
indicator-style="height:50px" indicator-style="height:50px"
v-if="showCol.am_pm"
> >
<picker-view-column> <picker-view-column v-if="showCol.am_pm">
<view <view
v-for="(item, index) in ['上午','下午']" v-for="(item, index) in ['上午', '下午']"
:key="index" :key="index"
class="flex itemcel flex-row flex-row-center-center" class="flex itemcel flex-row flex-row-center-center"
> >
<text :style="{ color: store.tmStore.dark ? 'white' : 'black' }">
<text :style="{color:store.tmStore.dark?'white':'black'}">{{item}}</text> {{ item }}
</text>
</view> </view>
</picker-view-column> </picker-view-column>
</picker-view> </picker-view>
@ -177,7 +189,7 @@
* 时间选择 * 时间选择
* @description 嵌入在页面的时间选择器 * @description 嵌入在页面的时间选择器
*/ */
import { useTmpiniaStore } from "../../tool/lib/tmpinia"; import { useTmpiniaStore } from '../../tool/lib/tmpinia'
import { import {
computed, computed,
PropType, PropType,
@ -189,44 +201,44 @@ import {
watch, watch,
onUpdated, onUpdated,
Ref, Ref,
} from "vue"; } from 'vue'
import { showDetail, coltimeData, timeDetailType } from "./interface"; import { showDetail, coltimeData, timeDetailType } from './interface'
import * as dayjs from "../../tool/dayjs/esm"; import * as dayjs from '../../tool/dayjs/esm'
import { propsOpts } from "./props"; import { propsOpts } from './props'
import { import {
rangeTimeArray, rangeTimeArray,
getNowbyIndex, getNowbyIndex,
getIndexNowbydate, getIndexNowbydate,
checkNowDateisBetween, checkNowDateisBetween,
} from "./time"; } from './time'
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'
// #ifdef APP-PLUS-NVUE // #ifdef APP-PLUS-NVUE
const dom = uni.requireNativePlugin("dom"); const dom = uni.requireNativePlugin('dom')
// #endif // #endif
const proxy = getCurrentInstance()?.proxy ?? null; const proxy = getCurrentInstance()?.proxy ?? null
const store = useTmpiniaStore(); const store = useTmpiniaStore()
const emits = defineEmits(["update:modelValue", "update:modelStr", "change"]); const emits = defineEmits(['update:modelValue', 'update:modelStr', 'change'])
const tmTimeViewName = "tmTimeViewName"; const tmTimeViewName = 'tmTimeViewName'
const DayJs = dayjs.default; const DayJs = dayjs.default
const props = defineProps({ ...propsOpts }); const props = defineProps({ ...propsOpts })
const _nowtime = ref( const _nowtime = ref(
DayJs(checkNowDateisBetween(props.defaultValue, props.start, props.end)) DayJs(checkNowDateisBetween(props.defaultValue, props.start, props.end)),
); )
const _nowtimeValue = computed(() => _nowtime.value.format()); const _nowtimeValue = computed(() => _nowtime.value.format())
const show = ref(true); const show = ref(true)
const _startTime = computed(() => { const _startTime = computed(() => {
return DayJs(props.start).isValid() return DayJs(props.start).isValid()
? DayJs(props.start).format() ? DayJs(props.start).format()
: DayJs().subtract(3, "year").format(); : DayJs().subtract(3, 'year').format()
}); })
const _endTime = computed(() => { const _endTime = computed(() => {
return DayJs(props.end).isValid() return DayJs(props.end).isValid()
? DayJs(props.end).format() ? DayJs(props.end).format()
: DayJs().add(1, "year").format(); : DayJs().add(1, 'year').format()
}); })
const showCol = computed<showDetail>(() => { const showCol = computed<showDetail>(() => {
return { return {
year: props.showDetail?.year ?? true, year: props.showDetail?.year ?? true,
@ -235,24 +247,25 @@ const showCol = computed<showDetail>(() => {
hour: props.showDetail?.hour ?? false, hour: props.showDetail?.hour ?? false,
minute: props.showDetail?.minute ?? false, minute: props.showDetail?.minute ?? false,
second: props.showDetail?.second ?? false, second: props.showDetail?.second ?? false,
}; am_pm: props.showDetail?.am_pm ?? true,
}); }
})
const trueCount = computed(() => { const trueCount = computed(() => {
return Object.values(showCol.value).filter(value => value === true).length; return Object.values(showCol.value).filter((value) => value === true).length
}); })
const showSuffix = computed(() => { const showSuffix = computed(() => {
return { return {
year: props.showSuffix?.year ?? "年", year: props.showSuffix?.year ?? '年',
month: props.showSuffix?.month ?? "月", month: props.showSuffix?.month ?? '月',
hour: props.showSuffix?.hour ?? "时", hour: props.showSuffix?.hour ?? '时',
minute: props.showSuffix?.minute ?? "分", minute: props.showSuffix?.minute ?? '分',
second: props.showSuffix?.second ?? "秒", second: props.showSuffix?.second ?? '秒',
date: props.showSuffix?.day ?? "日", date: props.showSuffix?.day ?? '日',
}; }
}); })
const isDark = computed(() => store.tmStore.dark); const isDark = computed(() => store.tmStore.dark)
let colIndex: Ref<Array<number>> = ref([0, 0, 0, 0, 0, 0]); let colIndex: Ref<Array<number>> = ref([0, 0, 0, 0, 0, 0])
let ampmIndex: Ref<Array<number>> = ref([0]); let ampmIndex: Ref<Array<number>> = ref([0])
const _col = ref({ const _col = ref({
year: [] as Array<number>, year: [] as Array<number>,
month: [] as Array<number>, month: [] as Array<number>,
@ -260,129 +273,144 @@ const _col = ref({
hour: [] as Array<number>, hour: [] as Array<number>,
minute: [] as Array<number>, minute: [] as Array<number>,
second: [] as Array<number>, second: [] as Array<number>,
}); })
let timid = NaN; let timid = NaN
const maskWidth = ref(0); const maskWidth = ref(0)
const maskHeight = computed(() => { const maskHeight = computed(() => {
return (uni.upx2px(props.height) - 50) / 2; return (uni.upx2px(props.height) - 50) / 2
}); })
const maskStyle = computed(() => { const maskStyle = computed(() => {
let str_white = let str_white =
"background-image:linear-gradient(rgba(255,255,255,0.95),rgba(255,255,255,0.6)),linear-gradient(rgba(255,255,255,0.6),rgba(255,255,255,0.95))"; 'background-image:linear-gradient(rgba(255,255,255,0.95),rgba(255,255,255,0.6)),linear-gradient(rgba(255,255,255,0.6),rgba(255,255,255,0.95))'
let str_black = let str_black =
"background-image:linear-gradient(rgba(17, 17, 17, 1.0),rgba(106, 106, 106, 0.2)),linear-gradient(rgba(106, 106, 106, 0.2),rgba(17, 17, 17, 1.0))"; 'background-image:linear-gradient(rgba(17, 17, 17, 1.0),rgba(106, 106, 106, 0.2)),linear-gradient(rgba(106, 106, 106, 0.2),rgba(17, 17, 17, 1.0))'
// #ifdef APP-NVUE // #ifdef APP-NVUE
str_black = str_black =
"background-image: linear-gradient(to bottom,rgba(30, 30, 30, 0.9),rgba(104, 104, 104, 0.6))"; 'background-image: linear-gradient(to bottom,rgba(30, 30, 30, 0.9),rgba(104, 104, 104, 0.6))'
// #endif // #endif
if (!isDark.value) { if (!isDark.value) {
return str_white; return str_white
} }
return str_black; return str_black
}); })
_col.value = rangeTimeArray( _col.value = rangeTimeArray(
_nowtimeValue.value, _nowtimeValue.value,
_startTime.value, _startTime.value,
_endTime.value, _endTime.value,
showCol.value showCol.value,
); )
function change1(data){ function change1(data) {
console.log('data',data) console.log('data', data)
} }
function colchange(e: any) { function colchange(e: any) {
let changedate = getNowbyIndex(
let changedate = getNowbyIndex(_col.value, e.detail.value, showCol.value, _startTime.value,_endTime.value); _col.value,
let testDate = checkNowDateisBetween(changedate, _startTime.value,_endTime.value) e.detail.value,
showCol.value,
_startTime.value,
_endTime.value,
)
let testDate = checkNowDateisBetween(
changedate,
_startTime.value,
_endTime.value,
)
let testRang = rangeTimeArray( let testRang = rangeTimeArray(
testDate, testDate,
_startTime.value, _startTime.value,
_endTime.value, _endTime.value,
showCol.value showCol.value,
); )
_nowtime.value = DayJs(testDate);
colIndex.value = getIndexNowbydate(testRang, _nowtime.value, showCol.value);
emits("update:modelValue", _nowtime.value.format("YYYY/MM/DD HH:mm:ss"));
emits("update:modelStr", _nowtime.value.format(props.format));
emits("change", _nowtime.value.format(props.format));
_col.value= testRang;
_nowtime.value = DayJs(testDate)
colIndex.value = getIndexNowbydate(testRang, _nowtime.value, showCol.value)
emits('update:modelValue', _nowtime.value.format('YYYY/MM/DD HH:mm:ss'))
emits('update:modelStr', _nowtime.value.format(props.format))
emits('change', _nowtime.value.format(props.format))
_col.value = testRang
} }
watch( watch(
() => props.modelValue, () => props.modelValue,
() => { () => {
if (!DayJs(props.modelValue).isValid()) return; if (!DayJs(props.modelValue).isValid()) return
let deattime = DayJs(checkNowDateisBetween(props.modelValue, props.start, props.end)); let deattime = DayJs(
checkNowDateisBetween(props.modelValue, props.start, props.end),
)
if (DayJs(deattime).isSame(_nowtime.value)) return; if (DayJs(deattime).isSame(_nowtime.value)) return
_nowtime.value = deattime; _nowtime.value = deattime
emits("update:modelStr", _nowtime.value.format(props.format)); emits('update:modelStr', _nowtime.value.format(props.format))
// #ifdef APP-NVUE // #ifdef APP-NVUE
_col.value = rangeTimeArray( _col.value = rangeTimeArray(
deattime, deattime,
_startTime.value, _startTime.value,
_endTime.value, _endTime.value,
showCol.value showCol.value,
); )
show.value = false; show.value = false
colIndex.value = getIndexNowbydate(_col.value, _nowtime.value, showCol.value); colIndex.value = getIndexNowbydate(
_col.value,
_nowtime.value,
showCol.value,
)
nextTick(() => { nextTick(() => {
/**uni sdk3.6.8nvuepicker viewvalue /**uni sdk3.6.8nvuepicker viewvalue
* 其它平台没有这问题 * 其它平台没有这问题
*/ */
show.value = true; show.value = true
}); })
// #endif // #endif
// #ifndef APP-NVUE // #ifndef APP-NVUE
_col.value = rangeTimeArray( _col.value = rangeTimeArray(
deattime, deattime,
_startTime.value, _startTime.value,
_endTime.value, _endTime.value,
showCol.value showCol.value,
); )
colIndex.value = getIndexNowbydate(_col.value, _nowtime.value, showCol.value); colIndex.value = getIndexNowbydate(
_col.value,
_nowtime.value,
showCol.value,
)
// #endif // #endif
} },
); )
function nvuegetClientRect() { function nvuegetClientRect() {
nextTick(function () { nextTick(function () {
// #ifdef APP-PLUS-NVUE // #ifdef APP-PLUS-NVUE
dom.getComponentRect(proxy.$refs.picker, function (res) { dom.getComponentRect(proxy.$refs.picker, function (res) {
if (res?.size) { if (res?.size) {
maskWidth.value = res.size.width; maskWidth.value = res.size.width
if (res.size.width == 0) { if (res.size.width == 0) {
nvuegetClientRect(); nvuegetClientRect()
} }
} }
}); })
// #endif // #endif
}); })
} }
onMounted(() => { onMounted(() => {
nvuegetClientRect(); nvuegetClientRect()
nextTick(() => { nextTick(() => {
emits("update:modelValue", _nowtime.value.format("YYYY/MM/DD HH:mm:ss")); emits('update:modelValue', _nowtime.value.format('YYYY/MM/DD HH:mm:ss'))
emits("update:modelStr", _nowtime.value.format(props.format)); emits('update:modelStr', _nowtime.value.format(props.format))
colIndex.value = getIndexNowbydate(_col.value, _nowtime.value, showCol.value); colIndex.value = getIndexNowbydate(
}); _col.value,
}); _nowtime.value,
showCol.value,
)
})
})
onUpdated(() => nvuegetClientRect())
onUpdated(() => nvuegetClientRect());
// defineExpose({tmTimeViewName,setNowtime}) // defineExpose({tmTimeViewName,setNowtime})
</script> </script>
@ -397,7 +425,11 @@ onUpdated(() => nvuegetClientRect());
} }
.bottom { .bottom {
background-image: linear-gradient(to top, rgba(17, 17, 17, 1), rgba(36, 36, 36, 0.6)); background-image: linear-gradient(
to top,
rgba(17, 17, 17, 1),
rgba(36, 36, 36, 0.6)
);
} }
.itemcel { .itemcel {

View File

@ -1,76 +1,120 @@
{ {
"language": "简体-中国", "language": "简体-中国",
"index.search.subtext": "全端兼容vue3 TypeScript pinia组件库", "index.search.subtext": "全端兼容vue3 TypeScript pinia组件库",
"index.search.tips": "组件中文/英文名称", "index.search.tips": "组件中文/英文名称",
"index.search.btntext": "搜索组件", "index.search.btntext": "搜索组件",
"index.com.navtitle": "TMUI 全平台组件库", "index.com.navtitle": "TMUI 全平台组件库",
"index.com.title": "分类导航", "index.com.title": "分类导航",
"index.com.tongyong": "通用组件", "index.com.tongyong": "通用组件",
"index.com.tongyongSub": "高频常用组件", "index.com.tongyongSub": "高频常用组件",
"index.com.row": "布局组件", "index.com.row": "布局组件",
"index.com.rowSub": "布局排版", "index.com.rowSub": "布局排版",
"index.com.show": "展示组件", "index.com.show": "展示组件",
"index.com.showSub": "常见数据展示", "index.com.showSub": "常见数据展示",
"index.com.form": "表单录入", "index.com.form": "表单录入",
"index.com.formSub": "数据提交类", "index.com.formSub": "数据提交类",
"index.com.fd": "反馈类型", "index.com.fd": "反馈类型",
"index.com.fdSub": "提示弹层类组件", "index.com.fdSub": "提示弹层类组件",
"index.com.nav": "导航类型", "index.com.nav": "导航类型",
"index.com.navSub": "分页导航类", "index.com.navSub": "分页导航类",
"index.com.yewu": "业务型组件", "index.com.yewu": "业务型组件",
"index.com.yewuSub": "优惠券导购类", "index.com.yewuSub": "优惠券导购类",
"index.com.other": "其它", "index.com.other": "其它",
"index.com.otherSub": "功能型组件", "index.com.otherSub": "功能型组件",
"index.com.tubiao": "图表组件", "index.com.tubiao": "图表组件",
"index.com.tubiaoSub": "Echarts 5.3.2", "index.com.tubiaoSub": "Echarts 5.3.2",
"index.com.render": "tmCv", "index.com.render": "tmCv",
"index.com.renderSub": "canvas动画渲染", "index.com.renderSub": "canvas动画渲染",
"index.com.pag": "PAG", "index.com.pag": "PAG",
"index.com.pagSub": "腾讯pag动画", "index.com.pagSub": "腾讯pag动画",
"index.com.bottom": "TMUI3.0", "index.com.bottom": "TMUI3.0",
"index.com.setLocal": "设置语言", "index.com.setLocal": "设置语言",
"index.com.autoDark": "暗黑跟随系统", "index.com.autoDark": "暗黑跟随系统",
"index.com.love": "TMUI用户中心", "index.com.love": "TMUI用户中心",
"index.com.loveSub": "看广告赚积分", "index.com.loveSub": "看广告赚积分",
"index.com.themetext": "动态切换主题,默认主题见文档", "index.com.themetext": "动态切换主题,默认主题见文档",
"index.com.themeGreen": "小黄", "index.com.themeGreen": "小黄",
"index.com.themeBlue": "蓝色", "index.com.themeBlue": "蓝色",
"index.com.themeRed": "红色", "index.com.themeRed": "红色",
"index.com.themeDefault": "默认", "index.com.themeDefault": "默认",
"index.com.themeCustText": "自定", "index.com.themeCustText": "自定",
"message.load.text": "加载中", "message.load.text": "加载中",
"message.error.text": "操作错误", "message.error.text": "操作错误",
"message.info.text": "提示信息", "message.info.text": "提示信息",
"message.warn.text": "警告信息", "message.warn.text": "警告信息",
"message.quest.text": "似乎有问题", "message.quest.text": "似乎有问题",
"message.success.text": "操作成功", "message.success.text": "操作成功",
"message.disabled.text": "禁止操作", "message.disabled.text": "禁止操作",
"message.wait.text": "请稍候..", "message.wait.text": "请稍候..",
"index.mine.logOut":"退出登录", "index.mine.logOut": "退出登录",
"index.mine.help":"帮助中心", "index.mine.help": "帮助中心",
"index.mine.settings":"设置与隐私", "index.mine.settings": "设置与隐私",
"index.mine.addressBook":"通讯录", "index.mine.addressBook": "通讯录",
"index.mine.card":"我的名片", "index.mine.card": "我的名片",
"index.mine.information":"个人信息认证", "index.mine.information": "个人信息认证",
"index.mine.refresh":"刷新缓存", "index.mine.refresh": "刷新缓存",
"index.mine.clickRefresh":"点击清理缓存", "index.mine.clickRefresh": "点击清理缓存",
"index.mine.basic":"基本信息", "index.mine.basic": "基本信息",
"index.mine.company":"公司别", "index.mine.company": "公司别",
"index.mine.department":"部门", "index.mine.department": "部门",
"index.mine.post":"岗位", "index.mine.post": "岗位",
"index.mine.manager":"主管", "index.mine.manager": "主管",
"index.mine.phone":"手机号", "index.mine.phone": "手机号",
"index.mine.entry":"入职时间", "index.mine.entry": "入职时间",
"index.mine.unable":"无法上传照片", "index.mine.unable": "无法上传照片",
"index.mine.face":"扫脸失败", "index.mine.face": "扫脸失败",
"index.mine.recentPhoto":"个人近照", "index.mine.recentPhoto": "个人近照",
"userAgreement":"用户服务协议", "userAgreement": "用户服务协议",
"privacyPolicy":"隐私政策", "privacyPolicy": "隐私政策",
"index.mine.cancellation":"注销账号", "index.mine.cancellation": "注销账号",
"ok":"确定", "ok": "确定",
"message.customerService.text":"请拨打客服电话", "message.customerService.text": "请拨打客服电话",
"message.action.text":"进行注销操作", "message.action.text": "进行注销操作",
"index.mine.upload":"点击上传", "index.mine.upload": "点击上传",
"index.mine.reUpload":"重新上传", "index.mine.reUpload": "重新上传",
"index.mine.loading":"载入中" "index.mine.loading": "载入中",
"cancel": "取消",
"search.hint": "检索您要查找的内容吧~",
"search.chat.count": "条相关聊天记录",
"index.mine.project": "项目",
"chat.type.group": "群聊",
"chat.type.record": "聊天记录",
"search.result.include": "包含:",
"has_more": "更多",
"index.type.company": "公司",
"search.result.relevant": "相关",
"index.chat.settings": "聊天设置",
"chat.settings.clearChatRecord": "清空聊天记录",
"chat.settings.groupName": "群名称",
"chat.settings.groupNotice": "群公告",
"chat.settings.groupType": "群类型",
"chat.settings.topSession": "置顶会话",
"chat.settings.messageNoDisturb": "消息免打扰",
"chat.settings.groupGag": "群内禁言",
"chat.settings.groupAdmin": "群管理员",
"chat.settings.groupMember": "群成员",
"search.chat.record": "搜索聊天记录",
"record.searchType.date": "日期",
"record.searchType.imgAndVideo": "图片及视频",
"record.searchType.files": "文件",
"record.searchType.link": "链接",
"group.identify.admin": "管理员",
"group.disband.btn": "解散该群",
"group.quit.btn": "退出群聊",
"search.condition.date": "按日期查找",
"search.condition.date_pickerTitle": "请选择聊天日期",
"button.text.done": "完成",
"input.placeholder.enter": "请输入...",
"chat.settings.editGroupName": "修改群名称",
"edit.groupName.placeholder": "请输入群名称1~20个字",
"chat.settings.editAvatar": "修改头像",
"button.text.edit": "修改",
"chat.manage.silenceMember": "被禁言的成员",
"chat.manage.silenceAll": "全员禁言",
"chat.manage.silenceAllHint": "开启后,只允许群管理员发言",
"chat.manage.addSilenceMember": "添加禁言成员",
"chatSettings.btn.undoSilence": "解禁",
"silence.tag.hasDone": "已禁言",
"chat.manage.addAdmin": "添加管理员",
"search.condition.member": "按群成员查找"
} }