文件传输助手

This commit is contained in:
2026-09-23 17:54:51 +08:00
parent 08d8231671
commit 201d0086a4
5 changed files with 120 additions and 10 deletions
+50
View File
@@ -0,0 +1,50 @@
<template>
<view class="box">
<view class="sheet">
<view class="fold" />
<view class="line" />
<view class="line short" />
</view>
</view>
</template>
<script>
export default { name: 'FyFileMark' }
</script>
<style scoped>
.box {
width: 100%;
height: 100%;
background: #1677FF;
display: flex;
align-items: center;
justify-content: center;
}
.sheet {
width: 44%;
height: 54%;
background: #fff;
border-radius: 6rpx;
position: relative;
}
.fold {
position: absolute;
right: 0;
top: 0;
width: 32%;
height: 28%;
background: #D6E8FF;
border-bottom-left-radius: 6rpx;
}
.line {
position: absolute;
left: 16%;
right: 16%;
top: 50%;
height: 3rpx;
background: #1677FF;
border-radius: 3rpx;
}
.line.short { top: 66%; right: 38%; }
</style>
+53
View File
@@ -0,0 +1,53 @@
<template>
<wd-tabbar
v-model="active"
fixed
bordered
safe-area-inset-bottom
placeholder
active-color="#1677FF"
inactive-color="#8A9199"
@change="onChange"
>
<wd-tabbar-item title="消息" icon="chat" />
<wd-tabbar-item title="通讯录" icon="usergroup" />
<wd-tabbar-item title="打卡" icon="clock" />
<wd-tabbar-item title="工作台" icon="app" />
<wd-tabbar-item title="我" icon="user" />
</wd-tabbar>
</template>
<script>
const PATHS = [
'/pages/tabs/messages',
'/pages/tabs/contacts',
'/pages/tabs/clock',
'/pages/tabs/workbench',
'/pages/tabs/me'
]
export default {
data() {
return { active: 0 }
},
mounted() {
this.sync()
uni.$on('fy-tab', (i) => { this.active = i })
},
unmounted() {
uni.$off('fy-tab')
},
methods: {
sync() {
const pages = getCurrentPages()
const route = pages.length ? '/' + pages[pages.length - 1].route : ''
const i = PATHS.indexOf(route)
if (i >= 0) this.active = i
},
onChange({ value }) {
const url = PATHS[value]
if (url) uni.switchTab({ url })
}
}
}
</script>
+6 -2
View File
@@ -35,7 +35,8 @@
> >
<view v-if="selecting" class="ck" :class="{ on: isPicked(m) }" /> <view v-if="selecting" class="ck" :class="{ on: isPicked(m) }" />
<view v-if="!m.mine" class="av" @click.stop="openSender(m)"> <view v-if="!m.mine" class="av" @click.stop="openSender(m)">
<image v-if="avatarOf(m)" :src="avatarOf(m)" mode="aspectFill" /> <fy-file-mark v-if="isFiles" />
<image v-else-if="avatarOf(m)" :src="avatarOf(m)" mode="aspectFill" />
<text v-else>{{ initialOf(m) }}</text> <text v-else>{{ initialOf(m) }}</text>
</view> </view>
<view class="col" :class="{ mine: m.mine }"> <view class="col" :class="{ mine: m.mine }">
@@ -155,6 +156,7 @@
<script> <script>
import FyHeader from '../../components/fy-header.vue' import FyHeader from '../../components/fy-header.vue'
import FyFileMark from '../../components/fy-file-mark.vue'
import { store, previewOf } from '../../utils/store.js' import { store, previewOf } from '../../utils/store.js'
import { fmtTime, toast } from '../../utils/format.js' import { fmtTime, toast } from '../../utils/format.js'
import { uploadLocal, fileTicket, openFile } from '../../utils/files.js' import { uploadLocal, fileTicket, openFile } from '../../utils/files.js'
@@ -163,7 +165,7 @@ import { mobileUrl } from '../../utils/nav.js'
const EMOS = ['😀','😁','😂','😅','😊','😍','😘','😜','🤗','🤔','🙄','😴','😭','😡','👍','👎','👌','🙏','👏','💪','🔥','⭐','🎉','❤️','💕','✅','❌','💯','🤝','👀'] const EMOS = ['😀','😁','😂','😅','😊','😍','😘','😜','🤗','🤔','🙄','😴','😭','😡','👍','👎','👌','🙏','👏','💪','🔥','⭐','🎉','❤️','💕','✅','❌','💯','🤝','👀']
export default { export default {
components: { FyHeader }, components: { FyHeader, FyFileMark },
data() { data() {
return { return {
sid: '', sid: '',
@@ -174,6 +176,7 @@ export default {
title: '聊天', title: '聊天',
isGroup: false, isGroup: false,
isWork: false, isWork: false,
isFiles: false,
peerId: '', peerId: '',
myAvatar: '', myAvatar: '',
myInitial: '我', myInitial: '我',
@@ -289,6 +292,7 @@ export default {
this.title = (c && c.name) || '聊天' this.title = (c && c.name) || '聊天'
this.isGroup = !!(c && c.type === 'group') this.isGroup = !!(c && c.type === 'group')
this.isWork = !!(c && c.type === 'work') this.isWork = !!(c && c.type === 'work')
this.isFiles = !!(c && c.type === 'files')
this.peerId = (c && c.peerId) || '' this.peerId = (c && c.peerId) || ''
this.memberCount = c && c.memberIds ? c.memberIds.length : 0 this.memberCount = c && c.memberIds ? c.memberIds.length : 0
this.myAvatar = (store.me && store.me.avatar) || '' this.myAvatar = (store.me && store.me.avatar) || ''
+6 -3
View File
@@ -17,7 +17,8 @@
</view> </view>
<view v-if="convs.length" class="sec">会话</view> <view v-if="convs.length" class="sec">会话</view>
<view v-for="c in convs" :key="'c' + c.id" class="item" @click="openConv(c)"> <view v-for="c in convs" :key="'c' + c.id" class="item" @click="openConv(c)">
<view class="av">{{ (c.name || '?').slice(0, 1) }}</view> <view v-if="c.type === 'files'" class="av"><fy-file-mark /></view>
<view v-else class="av">{{ (c.name || '?').slice(0, 1) }}</view>
<view class="meta"> <view class="meta">
<text class="n">{{ c.name }}</text> <text class="n">{{ c.name }}</text>
<text class="s">{{ c.lastMessage }}</text> <text class="s">{{ c.lastMessage }}</text>
@@ -25,7 +26,8 @@
</view> </view>
<view v-if="hits.length" class="sec">聊天记录</view> <view v-if="hits.length" class="sec">聊天记录</view>
<view v-for="h in hits" :key="h.id" class="item" @click="openConv(h.conv)"> <view v-for="h in hits" :key="h.id" class="item" @click="openConv(h.conv)">
<view class="av">{{ (h.conv.name || '?').slice(0, 1) }}</view> <view v-if="h.conv.type === 'files'" class="av"><fy-file-mark /></view>
<view v-else class="av">{{ (h.conv.name || '?').slice(0, 1) }}</view>
<view class="meta"> <view class="meta">
<text class="n">{{ h.conv.name }}</text> <text class="n">{{ h.conv.name }}</text>
<text class="s">{{ h.text }}</text> <text class="s">{{ h.text }}</text>
@@ -45,10 +47,11 @@
<script> <script>
import FyHeader from '../../components/fy-header.vue' import FyHeader from '../../components/fy-header.vue'
import FyFileMark from '../../components/fy-file-mark.vue'
import { store, previewOf } from '../../utils/store.js' import { store, previewOf } from '../../utils/store.js'
export default { export default {
components: { FyHeader }, components: { FyHeader, FyFileMark },
data() { return { q: '' } }, data() { return { q: '' } },
computed: { computed: {
people() { people() {
+5 -5
View File
@@ -17,7 +17,8 @@
@longpress="act(c)" @longpress="act(c)"
> >
<view class="av-wrap"> <view class="av-wrap">
<image v-if="c.avatar" class="av" :src="c.avatar" mode="aspectFill" /> <image v-if="c.avatar && c.type !== 'files'" class="av" :src="c.avatar" mode="aspectFill" />
<view v-else-if="c.type === 'files'" class="av"><fy-file-mark /></view>
<view v-else class="av txt" :class="avClass(c)">{{ avText(c) }}</view> <view v-else class="av txt" :class="avClass(c)">{{ avText(c) }}</view>
<text v-if="c.unread && !c.muted" class="badge abs">{{ c.unread > 99 ? '99+' : c.unread }}</text> <text v-if="c.unread && !c.muted" class="badge abs">{{ c.unread > 99 ? '99+' : c.unread }}</text>
<text v-else-if="c.unread && c.muted" class="dotu" /> <text v-else-if="c.unread && c.muted" class="dotu" />
@@ -39,10 +40,11 @@
<script> <script>
import FyHeader from '../../components/fy-header.vue' import FyHeader from '../../components/fy-header.vue'
import FyFileMark from '../../components/fy-file-mark.vue'
import { store } from '../../utils/store.js' import { store } from '../../utils/store.js'
export default { export default {
components: { FyHeader }, components: { FyHeader, FyFileMark },
data() { data() {
return { rows: [], unread: 0, off: null } return { rows: [], unread: 0, off: null }
}, },
@@ -68,13 +70,11 @@ export default {
}, },
avText(c) { avText(c) {
if (c.type === 'work') return '工' if (c.type === 'work') return '工'
if (c.type === 'files') return '文'
if (c.type === 'group') return '群' if (c.type === 'group') return '群'
return (c.name || '?').slice(0, 1) return (c.name || '?').slice(0, 1)
}, },
avClass(c) { avClass(c) {
if (c.type === 'work') return 'work' if (c.type === 'work') return 'work'
if (c.type === 'files') return 'file'
return 'plain' return 'plain'
}, },
open(c) { open(c) {
@@ -132,7 +132,7 @@ export default {
.item { display: flex; align-items: center; padding: 22rpx 28rpx; background: #fff; } .item { display: flex; align-items: center; padding: 22rpx 28rpx; background: #fff; }
.item.pin { background: #F7F8FA; } .item.pin { background: #F7F8FA; }
.av-wrap { position: relative; width: 92rpx; height: 92rpx; flex-shrink: 0; } .av-wrap { position: relative; width: 92rpx; height: 92rpx; flex-shrink: 0; }
.av { width: 92rpx; height: 92rpx; border-radius: 20rpx; } .av { width: 92rpx; height: 92rpx; border-radius: 20rpx; overflow: hidden; }
.av.txt { display: flex; align-items: center; justify-content: center; font-size: 32rpx; font-weight: 600; } .av.txt { display: flex; align-items: center; justify-content: center; font-size: 32rpx; font-weight: 600; }
.av.work { background: #1677FF; color: #fff; } .av.work { background: #1677FF; color: #fff; }
.av.file { background: #F3F6FB; color: #4B5563; } .av.file { background: #F3F6FB; color: #4B5563; }