├── .vite
└── deps
│ ├── package.json
│ └── _metadata.json
├── src-tauri
├── build.rs
├── .gitignore
├── icons
│ ├── 32x32.png
│ ├── icon.icns
│ ├── icon.ico
│ ├── icon.png
│ ├── 128x128.png
│ ├── 128x128@2x.png
│ ├── StoreLogo.png
│ ├── Square30x30Logo.png
│ ├── Square44x44Logo.png
│ ├── Square71x71Logo.png
│ ├── Square89x89Logo.png
│ ├── Square107x107Logo.png
│ ├── Square142x142Logo.png
│ ├── Square150x150Logo.png
│ ├── Square284x284Logo.png
│ └── Square310x310Logo.png
├── src
│ └── main.rs
├── Cargo.toml
└── tauri.conf.json
├── public
├── hb.png
├── add.png
├── back.png
├── call.png
├── dark.png
├── gift.png
├── male.png
├── more.png
├── scan.png
├── audio.png
├── emoji+.png
├── emoji.png
├── favicon.ico
├── female.png
├── moment.png
├── money.png
├── scan-o.png
├── search.png
├── setting.png
├── unknown.png
├── volume.png
├── wallet.png
├── wechat.png
├── flash-off.png
├── flash-on.png
├── keyboard.png
├── audio-white.png
├── call-white.png
├── emoji-white.png
├── group-chat.png
├── group-create.png
├── more-white.png
├── new-friend.png
├── red-packet.png
├── volume-right.png
├── keyboard-white.png
├── red-packet-bg.jpg
├── friend-only-chat.png
├── red-packet-white.png
├── red-packet-record-bg.jpg
└── audio
│ └── notification
│ ├── call
│ ├── end.mp3
│ └── start.mp3
│ └── chat
│ ├── msg.mp3
│ └── audio.mp3
├── .env.example
├── src
├── assets
│ ├── bg.png
│ ├── home-bg.jpeg
│ ├── logo.svg
│ ├── animation.css
│ └── theme.css
├── components
│ ├── Verifition
│ │ ├── index.ts
│ │ └── src
│ │ │ ├── Verify
│ │ │ ├── index.ts
│ │ │ └── VerifyPoints.vue
│ │ │ └── utils
│ │ │ ├── ase.ts
│ │ │ └── util.ts
│ ├── common
│ │ ├── popup.vue
│ │ ├── background.vue
│ │ └── search.vue
│ ├── message
│ │ └── popup.vue
│ ├── friend
│ │ ├── setting.vue
│ │ ├── add.vue
│ │ ├── perm.vue
│ │ ├── remark.vue
│ │ └── new.vue
│ ├── chat
│ │ ├── group
│ │ │ ├── users.vue
│ │ │ ├── list.vue
│ │ │ ├── update.vue
│ │ │ └── action.vue
│ │ ├── friend
│ │ │ └── list.vue
│ │ ├── redPacket
│ │ │ ├── record.vue
│ │ │ ├── detail.vue
│ │ │ └── form.vue
│ │ ├── list.vue
│ │ └── info.vue
│ ├── me
│ │ ├── wallet.vue
│ │ └── setting.vue
│ └── discover
│ │ └── moment
│ │ ├── post.vue
│ │ └── message.vue
├── utils
│ ├── emitter.js
│ ├── call.js
│ ├── request.js
│ ├── moment.js
│ ├── websocket.js
│ ├── chat.js
│ └── helper.js
├── enums
│ ├── user.js
│ ├── moment.js
│ ├── file.js
│ ├── call.js
│ ├── redPacket.js
│ ├── friend.js
│ ├── message.js
│ └── app.js
├── api
│ ├── group.js
│ ├── system.js
│ ├── file.js
│ ├── message.js
│ ├── redPacket.js
│ ├── chat.js
│ ├── user.js
│ ├── friend.js
│ └── moment.js
├── stores
│ ├── user.js
│ └── app.js
├── main.js
├── views
│ ├── discover
│ │ └── index.vue
│ ├── chat
│ │ └── index.vue
│ ├── Login.vue
│ ├── me
│ │ ├── qrcode
│ │ │ ├── scan.vue
│ │ │ └── index.vue
│ │ └── index.vue
│ ├── friend
│ │ ├── index.vue
│ │ ├── apply.vue
│ │ └── info.vue
│ ├── Register.vue
│ └── Home.vue
├── router
│ └── index.js
└── App.vue
├── jsconfig.json
├── .gitignore
├── README.md
├── index.html
├── package.json
├── vite.config.js
└── LICENSE
/.vite/deps/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module"
3 | }
4 |
--------------------------------------------------------------------------------
/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::build()
3 | }
4 |
--------------------------------------------------------------------------------
/public/hb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/hb.png
--------------------------------------------------------------------------------
/public/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/add.png
--------------------------------------------------------------------------------
/public/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/back.png
--------------------------------------------------------------------------------
/public/call.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/call.png
--------------------------------------------------------------------------------
/public/dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/dark.png
--------------------------------------------------------------------------------
/public/gift.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/gift.png
--------------------------------------------------------------------------------
/public/male.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/male.png
--------------------------------------------------------------------------------
/public/more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/more.png
--------------------------------------------------------------------------------
/public/scan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/scan.png
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | VITE_APP_TITLE=仿微信
2 | VITE_APP_WEBSOCKET=ws://127.0.0.1:2346
3 | VITE_APP_DEBUG=true
--------------------------------------------------------------------------------
/public/audio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/audio.png
--------------------------------------------------------------------------------
/public/emoji+.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/emoji+.png
--------------------------------------------------------------------------------
/public/emoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/emoji.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/favicon.ico
--------------------------------------------------------------------------------
/public/female.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/female.png
--------------------------------------------------------------------------------
/public/moment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/moment.png
--------------------------------------------------------------------------------
/public/money.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/money.png
--------------------------------------------------------------------------------
/public/scan-o.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/scan-o.png
--------------------------------------------------------------------------------
/public/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/search.png
--------------------------------------------------------------------------------
/public/setting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/setting.png
--------------------------------------------------------------------------------
/public/unknown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/unknown.png
--------------------------------------------------------------------------------
/public/volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/volume.png
--------------------------------------------------------------------------------
/public/wallet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/wallet.png
--------------------------------------------------------------------------------
/public/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/wechat.png
--------------------------------------------------------------------------------
/src/assets/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src/assets/bg.png
--------------------------------------------------------------------------------
/public/flash-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/flash-off.png
--------------------------------------------------------------------------------
/public/flash-on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/flash-on.png
--------------------------------------------------------------------------------
/public/keyboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/keyboard.png
--------------------------------------------------------------------------------
/public/audio-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/audio-white.png
--------------------------------------------------------------------------------
/public/call-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/call-white.png
--------------------------------------------------------------------------------
/public/emoji-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/emoji-white.png
--------------------------------------------------------------------------------
/public/group-chat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/group-chat.png
--------------------------------------------------------------------------------
/public/group-create.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/group-create.png
--------------------------------------------------------------------------------
/public/more-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/more-white.png
--------------------------------------------------------------------------------
/public/new-friend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/new-friend.png
--------------------------------------------------------------------------------
/public/red-packet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/red-packet.png
--------------------------------------------------------------------------------
/public/volume-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/volume-right.png
--------------------------------------------------------------------------------
/src-tauri/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
--------------------------------------------------------------------------------
/src/assets/home-bg.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src/assets/home-bg.jpeg
--------------------------------------------------------------------------------
/src/components/Verifition/index.ts:
--------------------------------------------------------------------------------
1 | import Verify from "./src/Verify.vue";
2 |
3 | export { Verify };
4 |
--------------------------------------------------------------------------------
/public/keyboard-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/keyboard-white.png
--------------------------------------------------------------------------------
/public/red-packet-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/red-packet-bg.jpg
--------------------------------------------------------------------------------
/src-tauri/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/32x32.png
--------------------------------------------------------------------------------
/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/src-tauri/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/icon.png
--------------------------------------------------------------------------------
/src/utils/emitter.js:
--------------------------------------------------------------------------------
1 | //全局事件总线
2 | import mitt from 'mitt';
3 | const emitter = mitt();
4 | export default emitter;
--------------------------------------------------------------------------------
/public/friend-only-chat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/friend-only-chat.png
--------------------------------------------------------------------------------
/public/red-packet-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/red-packet-white.png
--------------------------------------------------------------------------------
/src-tauri/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/128x128.png
--------------------------------------------------------------------------------
/src-tauri/icons/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/128x128@2x.png
--------------------------------------------------------------------------------
/src-tauri/icons/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/StoreLogo.png
--------------------------------------------------------------------------------
/public/red-packet-record-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/red-packet-record-bg.jpg
--------------------------------------------------------------------------------
/src-tauri/icons/Square30x30Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/Square30x30Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square44x44Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/Square44x44Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square71x71Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/Square71x71Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square89x89Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/Square89x89Logo.png
--------------------------------------------------------------------------------
/src/enums/user.js:
--------------------------------------------------------------------------------
1 | const GenderName = {
2 | male: "男",
3 | female: "女",
4 | unknown: "未知",
5 | }
6 | export { GenderName }
--------------------------------------------------------------------------------
/public/audio/notification/call/end.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/audio/notification/call/end.mp3
--------------------------------------------------------------------------------
/public/audio/notification/chat/msg.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/audio/notification/chat/msg.mp3
--------------------------------------------------------------------------------
/src-tauri/icons/Square107x107Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/Square107x107Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square142x142Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/Square142x142Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square150x150Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/Square150x150Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square284x284Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/Square284x284Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square310x310Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/src-tauri/icons/Square310x310Logo.png
--------------------------------------------------------------------------------
/src/enums/moment.js:
--------------------------------------------------------------------------------
1 | export const ActionLike = 'like';
2 | export const ActionUnlike = 'unlike';
3 | export const ActionComment = 'comment';
--------------------------------------------------------------------------------
/public/audio/notification/call/start.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/audio/notification/call/start.mp3
--------------------------------------------------------------------------------
/public/audio/notification/chat/audio.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/cover-wechat-client/main/public/audio/notification/chat/audio.mp3
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | },
7 | "exclude": ["node_modules", "dist"]
8 | }
9 |
--------------------------------------------------------------------------------
/src/enums/file.js:
--------------------------------------------------------------------------------
1 | export const UploadMaxSize = {
2 | file: 5 * 1024 * 1024,
3 | image: 5 * 1024 * 1024,
4 | video: 30 * 1024 * 1024,
5 | audio: 10 * 1024 * 1024,
6 | };
7 |
--------------------------------------------------------------------------------
/src/components/Verifition/src/Verify/index.ts:
--------------------------------------------------------------------------------
1 | import VerifySlide from './VerifySlide.vue'
2 | import VerifyPoints from './VerifyPoints.vue'
3 |
4 | export { VerifySlide, VerifyPoints }
5 |
--------------------------------------------------------------------------------
/.vite/deps/_metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "hash": "6f37935d",
3 | "configHash": "a600e482",
4 | "lockfileHash": "e3b0c442",
5 | "browserHash": "216ac62f",
6 | "optimized": {},
7 | "chunks": {}
8 | }
--------------------------------------------------------------------------------
/src/enums/call.js:
--------------------------------------------------------------------------------
1 | export const StatusInclosing = "inclosing";
2 | export const StatusIncoming = "incoming";
3 | export const StatusInwaiting = "inwaiting";
4 | export const StatusIncalling = "incalling";
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/api/group.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 |
3 | //创建群聊或邀请好友进群
4 | export const postAction = async (data) => {
5 | return await request.post("/api/group/action", data);
6 | };
7 | //获取群聊列表
8 | export const getList = async () => {
9 | return await request.get("/api/group/list");
10 | };
11 |
--------------------------------------------------------------------------------
/src-tauri/src/main.rs:
--------------------------------------------------------------------------------
1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!!
2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3 |
4 | fn main() {
5 | tauri::Builder::default()
6 | .run(tauri::generate_context!())
7 | .expect("error while running tauri application");
8 | }
9 |
--------------------------------------------------------------------------------
/src/api/system.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 | //获取验证码
3 | export const getCodeApi = async (data) => {
4 | return await request.post("/api/system/captcha/get", data);
5 | };
6 |
7 | //验证码校验
8 | export const reqCheckApi = async (data) => {
9 | return await request.post("/api/system/captcha/check", data);
10 | };
11 |
--------------------------------------------------------------------------------
/src/enums/redPacket.js:
--------------------------------------------------------------------------------
1 | const TypeNormal = "normal";
2 | const TypeLucky = "lucky";
3 | const TypeBelong = "belong";
4 | const TypeName = {
5 | normal: "普通红包",
6 | lucky: "拼手气红包",
7 | belong: "专属红包"
8 | }
9 | const StatusName = {
10 | "-1": "已被领取",
11 | "-2": "已领取",
12 | "-3": "已过期",
13 | "-4": "无法领取",
14 | "-5": "被抢光了"
15 | }
16 | export {TypeNormal, TypeLucky, TypeBelong, TypeName, StatusName};
--------------------------------------------------------------------------------
/src/utils/call.js:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 |
3 | export const isCall = ref(false);
4 | export const caller = ref(null);
5 | export const callType = ref(null);
6 |
7 | export const startCall = async (type) => {
8 | isCall.value = true;
9 | callType.value = type;
10 | };
11 |
12 | export const overCall = async () => {
13 | isCall.value = false;
14 | };
15 |
16 | export const setCaller = async (data) => {
17 | caller.value = data;
18 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 | .vscode
10 | .history
11 | .env
12 |
13 | node_modules
14 | .DS_Store
15 | dist
16 | dist-ssr
17 | coverage
18 | *.local
19 |
20 | /cypress/videos/
21 | /cypress/screenshots/
22 |
23 | # Editor directories and files
24 | .vscode/*
25 | !.vscode/extensions.json
26 | .idea
27 | *.suo
28 | *.ntvs*
29 | *.njsproj
30 | *.sln
31 | *.sw?
32 |
33 | *.tsbuildinfo
34 |
--------------------------------------------------------------------------------
/src/api/file.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 |
3 | //文件上传
4 | export const upload = async (data, onUploadProgress) => {
5 | return await request.post("/api/file/upload", data, {
6 | headers: {
7 | "Content-Type": "multipart/form-data",
8 | },
9 | onUploadProgress: onUploadProgress,
10 | timeout: 300000,
11 | });
12 | };
13 | //文件base64上传
14 | export const uploadBase64 = async (data) => {
15 | return await request.post("/api/file/upload-base64", data);
16 | };
17 |
--------------------------------------------------------------------------------
/src/components/Verifition/src/utils/ase.ts:
--------------------------------------------------------------------------------
1 | import CryptoJS from 'crypto-js'
2 | /**
3 | * @word 要加密的内容
4 | * @keyWord String 服务器随机返回的关键字
5 | * */
6 | export function aesEncrypt(word, keyWord = 'XwKsGlMcdPMEhR1B') {
7 | const key = CryptoJS.enc.Utf8.parse(keyWord)
8 | const srcs = CryptoJS.enc.Utf8.parse(word)
9 | const encrypted = CryptoJS.AES.encrypt(srcs, key, {
10 | mode: CryptoJS.mode.ECB,
11 | padding: CryptoJS.pad.Pkcs7
12 | })
13 | return encrypted.toString()
14 | }
15 |
--------------------------------------------------------------------------------
/src/enums/friend.js:
--------------------------------------------------------------------------------
1 | export const Apply = "apply";
2 | export const Verify = "verify";
3 |
4 | export const ApplyStatus = {
5 | wait_check: "待验证",
6 | go_check: "去验证",
7 | pass: "已添加",
8 | overdue: "已过期",
9 | };
10 | export const RelationShip = {
11 | Owner: "owner",
12 | Friend: "friend",
13 | GoCheck: "go_check",
14 | WaitCheck: "wait_check",
15 | Apply: "apply",
16 | };
17 |
18 | export const Setting = {
19 | FriendPerm: {
20 | MomentAndStatus: { DontSeeHim: "0", DontLetHimSeeIt: "0" },
21 | SettingFriendPerm: "ALLOW_ALL",
22 | },
23 | };
24 |
--------------------------------------------------------------------------------
/src/api/message.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 |
3 | //获取聊天记录
4 | export const getList = async (toUser, isGroup) => {
5 | return await request.get(
6 | `/api/message/list?to_user=${toUser}&is_group=${isGroup}`
7 | );
8 | };
9 |
10 | //发送消息
11 | export const send = async (data) => {
12 | return await request.post("/api/message/send", data);
13 | };
14 |
15 | //标记已读消息
16 | export const read = async (data) => {
17 | return await request.put("/api/message/read", data);
18 | };
19 |
20 | //获取未读消息数
21 | export const getUnread = async () => {
22 | return await request.get("/api/message/unread");
23 | };
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #
仿微信客户端
2 |
3 | 在线体验:https://chat.juenfy.cn
4 | 测试账号:13006789001 ~ 13006789010 密码:123456
5 | 或者自行注册
6 |
7 | 服务端传送门:https://github.com/Juenfy/cover-wechat-api
8 |
9 | ### 项目介绍
10 | 技术栈:vue3+pinia+vant
11 |
12 | ### 项目运行
13 |
14 | ```shell
15 | git clone https://github.com/Juenfy/cover-wechat-client.git
16 | ```
17 |
18 | ```shell
19 | cd cover-wechat-client
20 | ```
21 |
22 | ```shell
23 | cp .env.example .env
24 | ```
25 |
26 | ```shell
27 | npm install
28 | ```
29 | 浏览器访问:
30 | ```shell
31 | npm run dev
32 | ```
33 | 桌面端:
34 | ```shell
35 | npm run tauri dev
36 | ```
37 | 桌面端打包:
38 | ```shell
39 | npm run tauri build
40 | ```
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/api/redPacket.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 |
3 | //发红包
4 | const send = async (data) => {
5 | return await request.post('/api/red-packet/send', data);
6 | }
7 |
8 | //红包详情
9 | const getDetail = async (id) => {
10 | return await request.get(`/api/red-packet/detail?id=${id}`);
11 | }
12 |
13 | //领取红包
14 | const receive = async (id) => {
15 | return await request.post(`/api/red-packet/receive`, {id: id});
16 | }
17 |
18 | //红包领取记录
19 | const getRecordList = async (id, page, limit) => {
20 | return await request.get(`/api/red-packet/record-list?id=${id}&page=${page}&limit=${limit}`);
21 | }
22 | export {
23 | send,
24 | getDetail,
25 | receive,
26 | getRecordList
27 | };
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const http = axios.create({
4 | timeout: 60000,
5 | });
6 |
7 | http.interceptors.request.use(
8 | (config) => {
9 | if (localStorage.getItem("accessToken")) {
10 | config.headers["Authorization"] =
11 | "Bearer " + localStorage.getItem("accessToken");
12 | }
13 | return config;
14 | },
15 | (error) => {
16 | return Promise.reject(error);
17 | }
18 | );
19 |
20 | http.interceptors.response.use(
21 | (response) => {
22 | const res = response.data;
23 | if ([401200, 400006].indexOf(res.code) != -1) {
24 | location.href = "/login?logout=1";
25 | }
26 | return res;
27 | },
28 | (error) => {
29 | return Promise.reject(error);
30 | }
31 | );
32 | export default http;
33 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 微信
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/api/chat.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 | //获取聊天列表
3 | export const getList = async () => {
4 | return await request.get("/api/chat/list");
5 | };
6 | //获取聊天信息
7 | export const getInfo = async (toUser, isGroup) => {
8 | return await request.get(
9 | `/api/chat/info?to_user=${toUser}&is_group=${isGroup}`
10 | );
11 | };
12 | //不显示聊天
13 | export const putHide = async (data) => {
14 | return await request.put("/api/chat/hide", data);
15 | };
16 | //置顶聊天
17 | export const putTop = async (data) => {
18 | return await request.put("/api/chat/top", data);
19 | };
20 | //更新聊天信息
21 | export const putUpdate = async (data) => {
22 | return await request.put("/api/chat/update", data);
23 | };
24 | //删除聊天
25 | export const putDelete = async (data) => {
26 | return await request.put("/api/chat/delete", data);
27 | };
28 |
--------------------------------------------------------------------------------
/src/enums/message.js:
--------------------------------------------------------------------------------
1 | export const ActionSend = "send";
2 |
3 | export const ActionApply = "apply";
4 |
5 | export const ActionLogout = "logout";
6 |
7 | export const ActionCall = "call";
8 |
9 | export const ActionAt = "at";
10 |
11 | export const TypeText = "text";
12 |
13 | export const TypeFile = "file";
14 |
15 | export const TypeEmoji = "emoji";
16 |
17 | export const TypeImage = "image";
18 |
19 | export const TypeVideo = "video";
20 |
21 | export const TypeAudio = "audio";
22 |
23 | export const TypeVideoCall = "video_call";
24 |
25 | export const TypeAudioCall = "audio_call";
26 |
27 | export const TypeRedPacket = "red_packet";
28 |
29 | export const TypeContent = {
30 | file: "[文件消息]",
31 | emoji: "[表情消息]",
32 | image: "[图片消息]",
33 | video: "[视频消息]",
34 | audio: "[语音消息]",
35 | video_call: "[视频通话]",
36 | audio_call: "[语音通话]",
37 | red_packet: "[红包消息]"
38 | };
--------------------------------------------------------------------------------
/src-tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "app"
3 | version = "0.1.0"
4 | description = "A Tauri App"
5 | authors = ["you"]
6 | license = ""
7 | repository = ""
8 | default-run = "app"
9 | edition = "2021"
10 | rust-version = "1.60"
11 |
12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13 |
14 | [build-dependencies]
15 | tauri-build = { version = "1.5.4", features = [] }
16 |
17 | [dependencies]
18 | serde_json = "1.0"
19 | serde = { version = "1.0", features = ["derive"] }
20 | tauri = { version = "1.7.2", features = [] }
21 |
22 | [features]
23 | # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
24 | # If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
25 | # DO NOT REMOVE!!
26 | custom-protocol = [ "tauri/custom-protocol" ]
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vchat-vue",
3 | "version": "0.0.0",
4 | "private": true,
5 | "type": "module",
6 | "scripts": {
7 | "tauri": "tauri",
8 | "dev": "vite --host 0.0.0.0",
9 | "build": "vite build",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@tauri-apps/cli": "^1.6.1",
14 | "@vitejs/plugin-basic-ssl": "^1.1.0",
15 | "axios": "^1.6.8",
16 | "crypto-js": "^4.2.0",
17 | "emoji-picker-element": "^1.21.3",
18 | "mitt": "^3.0.1",
19 | "pinia": "^2.1.7",
20 | "pinia-plugin-persistedstate": "^3.2.1",
21 | "qrcode": "^1.5.3",
22 | "si-grenoble": "^0.6.0",
23 | "vant": "^4.8.11",
24 | "vconsole": "^3.15.1",
25 | "vue": "^3.4.21",
26 | "vue-qrcode-reader": "^5.5.4",
27 | "vue-router": "^4.3.0",
28 | "vue-wechat-camera": "^1.0.4"
29 | },
30 | "devDependencies": {
31 | "@vitejs/plugin-vue": "^5.0.4",
32 | "less": "^4.2.0",
33 | "vite": "^5.2.8"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/api/user.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 | //注册
3 | export const postRegister = async (data) => {
4 | return await request.post("/api/user/register", data);
5 | };
6 |
7 | //登录
8 | export const postLogin = async (data) => {
9 | return await request.post("/api/user/login", data);
10 | };
11 |
12 | //退出登录
13 | export const postLogout = async () => {
14 | return await request.post("/api/user/logout");
15 | };
16 |
17 | //充值
18 | export const postCharge = async (money) => {
19 | return await request.post("/api/user/charge", {money});
20 | };
21 |
22 | //用户主页
23 | export const getHomeInfo = async (keywords) => {
24 | return await request.get(`/api/user/${keywords}/home`);
25 | };
26 |
27 | //更新用户信息
28 | export const putUpdate = async (data) => {
29 | return await request.put("/api/user/update", data);
30 | };
31 |
32 | //用户朋友圈
33 | export const getMomentList = async (id, page, limit) => {
34 | return await request.get(`/api/user/moments?user_id=${id}&page=${page}&limit=${limit}`);
35 | };
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from "node:url";
2 |
3 | import { defineConfig } from "vite";
4 | import vue from "@vitejs/plugin-vue";
5 | import basicSsl from "@vitejs/plugin-basic-ssl";
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | // plugins: [vue(), basicSsl()],
9 | plugins: [vue()],
10 | optimizeDeps: {
11 | include: ["mitt"],
12 | },
13 | resolve: {
14 | alias: {
15 | "@": fileURLToPath(new URL("./src", import.meta.url)),
16 | },
17 | },
18 | server: {
19 | port: 5173,
20 | strictPort: true,
21 | watch: {
22 | // 3. tell vite to ignore watching `src-tauri`
23 | ignored: ["**/src-tauri/**"],
24 | },
25 | proxy: {
26 | "/api/": {
27 | target: "http://localhost:8000/",
28 | changeOrigin: true,
29 | rewrite: (path) => path.replace(/^\/api\//, ""),
30 | },
31 | },
32 | },
33 | build: {
34 | chunkSizeWarningLimit: 1600,
35 | outDir: 'dist',
36 | },
37 | });
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Juenfy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/assets/animation.css:
--------------------------------------------------------------------------------
1 | .fade-enter-active,
2 | .fade-leave-active {
3 | transition: opacity 0.2s ease;
4 | }
5 | .fade-enter-from,
6 | .fade-leave-to {
7 | opacity: 0;
8 | }
9 | /* 确保过渡前后的状态 */
10 | .fade-enter {
11 | opacity: 0;
12 | }
13 | .fade-enter-to {
14 | opacity: 1;
15 | }
16 | .fade-leave {
17 | opacity: 1;
18 | }
19 | .fade-leave-to {
20 | opacity: 0;
21 | }
22 | .slide-left-enter-active,
23 | .slide-left-leave-active {
24 | transition: transform 0.2s ease;
25 | }
26 | .slide-left-enter-from,
27 | .slide-left-leave-to {
28 | transform: translateX(100%);
29 | }
30 | /* 确保过渡前后的状态 */
31 | .slide-left-enter {
32 | opacity: 0;
33 | }
34 | .slide-left-enter-to {
35 | opacity: 1;
36 | }
37 | .slide-left-leave {
38 | opacity: 1;
39 | }
40 | .slide-left-leave-to {
41 | opacity: 0;
42 | }
43 | .bounce-enter-active {
44 | animation: bounce-in 0.5s;
45 | }
46 | .bounce-leave-active {
47 | animation: bounce-in 0.5s reverse;
48 | }
49 | @keyframes bounce-in {
50 | 0% {
51 | transform: scale(0);
52 | }
53 | 50% {
54 | transform: scale(1.25);
55 | }
56 | 100% {
57 | transform: scale(1);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/common/popup.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 | 完成
22 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/stores/user.js:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 | import { defineStore } from "pinia";
3 | import bg from "../assets/bg.png"
4 | export const useUserStore = defineStore(
5 | "user",
6 | () => {
7 | const isLogin = ref(false);
8 | const info = ref({});
9 | const homeInfo = ref({});
10 |
11 | const handleLogin = (user) => {
12 | if(user.moment_bg_file_path.trim().length <= 0)
13 | user.moment_bg_file_path = bg;
14 | info.value = user;
15 | localStorage.setItem("accessToken", user.token);
16 | isLogin.value = true;
17 | };
18 |
19 | const handleLogout = () => {
20 | info.value = {};
21 | localStorage.removeItem("accessToken");
22 | isLogin.value = false;
23 | };
24 |
25 | const updateInfo = (key, val) => {
26 | info.value[key] = val;
27 | };
28 |
29 | const setHomeInfo = (data) => {
30 | homeInfo.value = data;
31 | };
32 |
33 | return {
34 | handleLogin,
35 | handleLogout,
36 | updateInfo,
37 | setHomeInfo,
38 | isLogin,
39 | info,
40 | homeInfo
41 | };
42 | },
43 | { persist: true }
44 | );
45 |
--------------------------------------------------------------------------------
/src/api/friend.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 | //好友列表
3 | export const getList = async (type = 'all') => {
4 | return await request.get(`/api/friend/list?type=${type}`);
5 | };
6 |
7 | //朋友申请列表
8 | export const getApplyList = async () => {
9 | return await request.get("/api/friend/apply-list");
10 | };
11 |
12 | //删除好友申请
13 | export const deleteApply = async (id) => {
14 | return await request.delete(`/api/friend/delete-apply/${id}`);
15 | };
16 |
17 | //查找好友
18 | export const getSearchList = async (keywords) => {
19 | return await request.get(`/api/friend/search/${keywords}`);
20 | };
21 |
22 | //申请好友确认界面
23 | export const showConfirm = async (data) => {
24 | return await request.post("/api/friend/show-confirm", data);
25 | };
26 |
27 | //发送好友申请
28 | export const postApply = async (data) => {
29 | return await request.post("/api/friend/apply", data);
30 | };
31 |
32 | //通过好友验证
33 | export const postVerify = async (data) => {
34 | return await request.post("/api/friend/verify", data);
35 | };
36 |
37 | //更新好友
38 | export const putUpdate = async (data) => {
39 | return await request.put("/api/friend/update", data);
40 | };
41 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import { createPinia } from "pinia";
3 | //1.引入piniaPersistedstate持久化插件
4 | import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
5 | import App from "./App.vue";
6 | import router from "./router";
7 | import Vant from "vant";
8 | import VConsole from "vconsole";
9 |
10 | if (import.meta.env.VITE_APP_DEBUG === "true") {
11 | const vconsole = new VConsole();
12 | }
13 |
14 | import "@/assets/animation.css";
15 | import "@/assets/style.less";
16 | import "vant/lib/index.css";
17 | import "@/assets/theme.css";
18 | import { SystemAudio } from "@/enums/app";
19 |
20 | const pinia = createPinia();
21 | pinia.use(piniaPluginPersistedstate);
22 |
23 | const app = createApp(App);
24 |
25 | //加载系统音效
26 | const SysAudio = {};
27 | Object.keys(SystemAudio).forEach(pk => {
28 | Object.keys(SystemAudio[pk]).forEach(sk => {
29 | if (!SysAudio.hasOwnProperty(pk)) {
30 | SysAudio[pk] = {};
31 | }
32 | SysAudio[pk][sk] = new Audio(SystemAudio[pk][sk]);
33 | })
34 | });
35 | app.provide("SysAudio", SysAudio);
36 |
37 | app.use(pinia);
38 | app.use(router);
39 | app.use(Vant);
40 |
41 | app.mount("#app");
42 |
--------------------------------------------------------------------------------
/src/api/moment.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 |
3 | //发布朋友圈
4 | export const publish = async (data, onUploadProgress) => {
5 | return await request.post("/api/moment/publish", data, {
6 | headers: {
7 | "Content-Type": "multipart/form-data",
8 | },
9 | onUploadProgress: onUploadProgress,
10 | timeout: 300000,
11 | });
12 | };
13 | //获取朋友圈列表
14 | export const getList = async (page, limit) => {
15 | return await request.get(`/api/moment/list?page=${page}&limit=${limit}`);
16 | };
17 | //获取朋友圈详情
18 | export const getDetail = async (id) => {
19 | return await request.get(`/api/moment/detail?id=${id}`);
20 | }
21 |
22 | //获取点赞、评论消息列表
23 | export const getMessageList = async (page, limit) => {
24 | return await request.get(`/api/moment/message?page=${page}&limit=${limit}`);
25 | };
26 | //点赞朋友圈
27 | export const like = async (id) => {
28 | return await request.post('/api/moment/like', { id: id });
29 | };
30 | //取消点赞
31 | export const unlike = async (id) => {
32 | return await request.delete(`/api/moment/unlike?id=${id}`);
33 | };
34 | //评论
35 | export const comment = async (data) => {
36 | return await request.post('/api/moment/comment', data);
37 | };
38 | //删除朋友圈
39 | export const del = async (id) => {
40 | return await request.delete(`/api/moment/delete?id=${id}`);
41 | };
--------------------------------------------------------------------------------
/src/utils/moment.js:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 |
3 | export const momentList = ref([]);
4 |
5 | export const likeMoment = (data) => {
6 | momentList.value.forEach((item, index) => {
7 | if (item.id == data.moment_id) {
8 | momentList.value[index].likes.push(data);
9 | momentList.value[index].actions = [
10 | { text: '取消', value: 'unlike', icon: 'like' },
11 | { text: "评论", value: 'comment', icon: 'comment-o' },
12 | ];
13 | }
14 | });
15 | }
16 |
17 | export const unlikeMoment = (data) => {
18 | momentList.value.forEach((item, i) => {
19 | if (item.id == data.moment_id) {
20 | momentList.value[i].likes.forEach((like, j) => {
21 | if (like.id == data.like_id) {
22 | momentList.value[i].likes.splice(j, 1);
23 | momentList.value[i].actions = [
24 | { text: '赞', value: 'like', icon: 'like-o' },
25 | { text: "评论", value: 'comment', icon: 'comment-o' },
26 | ];
27 | }
28 | })
29 | }
30 | })
31 | }
32 |
33 | export const commentMoment = (data) => {
34 | momentList.value.forEach((item, i) => {
35 | if (item.id == data.moment_id) {
36 | momentList.value[i].comments.push(data);
37 | }
38 | })
39 | }
--------------------------------------------------------------------------------
/src/views/discover/index.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 朋友圈
20 |
21 |
22 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 扫一扫
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/views/chat/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/enums/app.js:
--------------------------------------------------------------------------------
1 | export const SearchFriend = "search-friend";
2 |
3 | export const CreateGroup = "create-group";
4 |
5 | export const QrcodeScan = "qrcode-scan";
6 |
7 | export const SearchChatRecord = "search-chat-record";
8 |
9 | export const SearchGroup = "search-group";
10 |
11 | export const SearchOnlyChatFriend = "search-only-chat-friend";
12 |
13 | export const Home = "home";
14 |
15 | export const HomeActions = [
16 | { text: "发起群聊", value: CreateGroup },
17 | { text: "添加朋友", value: SearchFriend },
18 | { text: "扫一扫", value: QrcodeScan },
19 | ];
20 |
21 | export const UnreadChat = "unread-chat";
22 | export const UnreadApply = "unread-apply";
23 | export const UnreadMoment = "unread-moment";
24 | export const Unread = [UnreadChat, UnreadApply, UnreadMoment];
25 |
26 | /* 主题图标 */
27 | export const ThemeIcon = {
28 | light: {
29 | emoji: "/emoji.png",
30 | audio: "/audio.png",
31 | more: "/more.png",
32 | keyboard: "/keyboard.png",
33 | call: "/call.png",
34 | red_packet: "/red-packet.png"
35 | },
36 | dark: {
37 | emoji: "/emoji-white.png",
38 | audio: "/audio-white.png",
39 | more: "/more-white.png",
40 | keyboard: "/keyboard-white.png",
41 | call: "/call-white.png",
42 | red_packet: "/red-packet-white.png"
43 | }
44 | }
45 |
46 | /* 系统音效 */
47 | export const SystemAudio = {
48 | chat: {
49 | audio: "/audio/notification/chat/audio.mp3",
50 | msg: "/audio/notification/chat/msg.mp3"
51 | },
52 | call: {
53 | start: "/audio/notification/call/start.mp3",
54 | end: "/audio/notification/call/end.mp3"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src-tauri/tauri.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../node_modules/@tauri-apps/cli/schema.json",
3 | "build": {
4 | "beforeBuildCommand": "npm run build",
5 | "beforeDevCommand": "npm run dev",
6 | "devPath": "http://localhost:5173",
7 | "distDir": "../dist"
8 | },
9 | "package": {
10 | "productName": "仿微信",
11 | "version": "0.1.0"
12 | },
13 | "tauri": {
14 | "allowlist": {
15 | "all": false
16 | },
17 | "bundle": {
18 | "active": true,
19 | "category": "DeveloperTool",
20 | "copyright": "",
21 | "deb": {
22 | "depends": []
23 | },
24 | "externalBin": [],
25 | "icon": [
26 | "icons/32x32.png",
27 | "icons/128x128.png",
28 | "icons/128x128@2x.png",
29 | "icons/icon.icns",
30 | "icons/icon.ico"
31 | ],
32 | "identifier": "com.tauri.build",
33 | "longDescription": "",
34 | "macOS": {
35 | "entitlements": null,
36 | "exceptionDomain": "",
37 | "frameworks": [],
38 | "providerShortName": null,
39 | "signingIdentity": null
40 | },
41 | "resources": [],
42 | "shortDescription": "",
43 | "targets": "all",
44 | "windows": {
45 | "certificateThumbprint": null,
46 | "digestAlgorithm": "sha256",
47 | "timestampUrl": "",
48 | "wix": {
49 | "language": "zh-CN"
50 | }
51 | }
52 | },
53 | "security": {
54 | "csp": null
55 | },
56 | "updater": {
57 | "active": false
58 | },
59 | "windows": [
60 | {
61 | "fullscreen": false,
62 | "height": 800,
63 | "resizable": true,
64 | "title": "仿微信",
65 | "width": 450
66 | }
67 | ]
68 | }
69 | }
--------------------------------------------------------------------------------
/src/utils/websocket.js:
--------------------------------------------------------------------------------
1 | import { ActionCall } from "@/enums/message";
2 | import emitter from "@/utils/emitter";
3 | let ws = null;
4 | class WebSocketClient {
5 | constructor(url) {
6 | this.url = url;
7 | this.websocket = null;
8 | }
9 |
10 | start(uid) {
11 | if ("WebSocket" in window) {
12 | console.log("当前浏览器支持 WebSocket");
13 | this.websocket = new WebSocket(this.url);
14 | } else if ("MozWebSocket" in window) {
15 | console.log("当前浏览器支持 MozWebSocket");
16 | this.websocket = new MozWebSocket(this.url);
17 | } else {
18 | alert("当前浏览器不支持 WebSocket");
19 | }
20 | if (this.websocket) {
21 | this.websocket.onopen = (e) => {
22 | console.log("连接成功", e);
23 | //登录成功 绑定uid
24 | const data = {
25 | who: "user",
26 | action: "login",
27 | data: { uid: uid },
28 | };
29 | this.send(data);
30 | };
31 | this.websocket.onmessage = (e) => {
32 | const data = JSON.parse(e.data);
33 | // console.log("收到消息", data);
34 | if (data.action === ActionCall) {
35 | emitter.emit("onCallMessage", data);
36 | } else {
37 | emitter.emit("onMessage", data);
38 | }
39 | };
40 |
41 | this.websocket.onclose = (e) => {
42 | console.log("连接关闭", e);
43 | };
44 | }
45 | }
46 |
47 | stop() {
48 | if (this.websocket) {
49 | this.websocket.close();
50 | }
51 | }
52 |
53 | send(data) {
54 | if (this.websocket) {
55 | data = typeof data == "object" ? JSON.stringify(data) : data;
56 | console.log("发送消息", data);
57 | this.websocket.send(data);
58 | }
59 | }
60 | }
61 | if (ws == null)
62 | ws = new WebSocketClient(import.meta.env.VITE_APP_WEBSOCKET);
63 | export default ws;
64 |
65 |
--------------------------------------------------------------------------------
/src/components/message/popup.vue:
--------------------------------------------------------------------------------
1 |
2 |
53 |
54 |
55 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/components/Verifition/src/utils/util.ts:
--------------------------------------------------------------------------------
1 | export function resetSize(vm) {
2 | let img_width, img_height, bar_width, bar_height; //图片的宽度、高度,移动条的宽度、高度
3 | const EmployeeWindow = window as any;
4 | const parentWidth =
5 | vm.$el.parentNode.offsetWidth || EmployeeWindow.offsetWidth;
6 | const parentHeight =
7 | vm.$el.parentNode.offsetHeight || EmployeeWindow.offsetHeight;
8 | if (vm.imgSize.width.indexOf("%") != -1) {
9 | img_width = (parseInt(vm.imgSize.width) / 100) * parentWidth + "px";
10 | } else {
11 | img_width = vm.imgSize.width;
12 | }
13 |
14 | if (vm.imgSize.height.indexOf("%") != -1) {
15 | img_height = (parseInt(vm.imgSize.height) / 100) * parentHeight + "px";
16 | } else {
17 | img_height = vm.imgSize.height;
18 | }
19 |
20 | if (vm.barSize.width.indexOf("%") != -1) {
21 | bar_width = (parseInt(vm.barSize.width) / 100) * parentWidth + "px";
22 | } else {
23 | bar_width = vm.barSize.width;
24 | }
25 |
26 | if (vm.barSize.height.indexOf("%") != -1) {
27 | bar_height = (parseInt(vm.barSize.height) / 100) * parentHeight + "px";
28 | } else {
29 | bar_height = vm.barSize.height;
30 | }
31 |
32 | return {
33 | imgWidth: img_width,
34 | imgHeight: img_height,
35 | barWidth: bar_width,
36 | barHeight: bar_height,
37 | };
38 | }
39 |
40 | export const _code_chars = [
41 | 1,
42 | 2,
43 | 3,
44 | 4,
45 | 5,
46 | 6,
47 | 7,
48 | 8,
49 | 9,
50 | "a",
51 | "b",
52 | "c",
53 | "d",
54 | "e",
55 | "f",
56 | "g",
57 | "h",
58 | "i",
59 | "j",
60 | "k",
61 | "l",
62 | "m",
63 | "n",
64 | "o",
65 | "p",
66 | "q",
67 | "r",
68 | "s",
69 | "t",
70 | "u",
71 | "v",
72 | "w",
73 | "x",
74 | "y",
75 | "z",
76 | "A",
77 | "B",
78 | "C",
79 | "D",
80 | "E",
81 | "F",
82 | "G",
83 | "H",
84 | "I",
85 | "J",
86 | "K",
87 | "L",
88 | "M",
89 | "N",
90 | "O",
91 | "P",
92 | "Q",
93 | "R",
94 | "S",
95 | "T",
96 | "U",
97 | "V",
98 | "W",
99 | "X",
100 | "Y",
101 | "Z",
102 | ];
103 | export const _code_color1 = ["#fffff0", "#f0ffff", "#f0fff0", "#fff0f0"];
104 | export const _code_color2 = [
105 | "#FF0033",
106 | "#006699",
107 | "#993366",
108 | "#FF9900",
109 | "#66CC66",
110 | "#FF33CC",
111 | ];
112 |
--------------------------------------------------------------------------------
/src/utils/chat.js:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 | import * as chatApi from "@/api/chat";
3 | import * as messageApi from "@/api/message";
4 | import { TypeImage } from "@/enums/message";
5 |
6 | export const chatInfo = ref({});
7 | export const chatList = ref([]);
8 | export const messageList = ref([]);
9 | export const atMessageIdList = ref([]);
10 | export const imagePreviewList = ref([]);
11 | //获取聊天记录
12 | export const getMessageList = async (params, cb) => {
13 | messageApi.getList(params.to_user, params.is_group).then((res) => {
14 | console.log("getMessageList", res);
15 | if (res.code == 200001) {
16 | imagePreviewList.value = [];
17 | atMessageIdList.value = [];
18 | messageList.value = res.data;
19 | messageList.value.forEach((item) => {
20 | if (item.type === TypeImage) imagePreviewList.value.push(item.content);
21 | if (item.at_users.indexOf(params.from_user) != -1) {
22 | atMessageIdList.value.push(item.id);
23 | }
24 | });
25 | cb(res);
26 | }
27 | });
28 | };
29 |
30 | export const updateRedPacketStatus = (msg, redPacketId) => {
31 | let status = 0;
32 | if (msg.indexOf("被领取") != -1) {
33 | status = -1;
34 | }
35 | if (msg.indexOf("已领取") != -1) {
36 | status = -2;
37 | }
38 | if (msg.indexOf("24小时") != -1) {
39 | status = -3;
40 | }
41 | if (msg.indexOf("无法领取") != -1) {
42 | status = -4;
43 | }
44 | if (msg.indexOf("手慢了") != -1) {
45 | status = -5;
46 | }
47 | messageList.value.forEach((item,index) => {
48 | if (item.red_packet_id == redPacketId) {
49 | messageList.value[index].red_packet.status = status;
50 | }
51 | });
52 | }
53 |
54 | //获取聊天信息
55 | export const getChatInfo = async (params, cb) => {
56 | chatApi.getInfo(params.to_user, params.is_group).then((res) => {
57 | console.log("getChatInfo", res);
58 | if (res.code == 200001) {
59 | chatInfo.value = res.data;
60 | cb(res);
61 | }
62 | });
63 | }
64 |
65 | //获取聊天列表
66 | export const getChatList = async () => {
67 | chatApi.getList().then((res) => {
68 | console.log("getChatList", res);
69 | chatList.value = sortChatList(res.data);
70 | });
71 | };
72 |
73 | //聊天列表排序
74 | export const sortChatList = (list) => {
75 | list.sort((a, b) => {
76 | if (a.top !== b.top) {
77 | return b.top - a.top;
78 | }
79 | return b.time - a.time;
80 | });
81 | return list;
82 | };
--------------------------------------------------------------------------------
/src/components/friend/setting.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 |
27 |
28 |
29 |
30 |
31 |
40 |
48 |
49 |
50 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | 删除联系人
80 |
81 |
82 |
83 |
84 |
85 |
92 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from "vue-router";
2 | import ChatIndex from "@/views/chat/index.vue";
3 | import FriendIndex from "@/views/friend/index.vue";
4 | import DiscoverIndex from "@/views/discover/index.vue";
5 | import MeIndex from "@/views/me/index.vue";
6 | import Home from "@/views/Home.vue";
7 | import ChatMessage from "@/views/chat/message.vue";
8 | import FriendInfo from "@/views/friend/info.vue";
9 | import FriendMoment from "@/views/friend/moment.vue";
10 | import DiscoverMoment from "@/views/discover/moment.vue";
11 | import QrcodeScan from "@/views/me/qrcode/scan.vue";
12 | import Register from "@/views/Register.vue";
13 | import Login from "@/views/Login.vue";
14 | import FriendApply from "@/views/friend/apply.vue";
15 | import QrcodeIndex from "@/views/me/qrcode/index.vue";
16 | const router = createRouter({
17 | history: createWebHistory(import.meta.env.BASE_URL),
18 | routes: [
19 | {
20 | path: "/",
21 | name: "home",
22 | component: Home,
23 | children: [
24 | {
25 | path: "chat",
26 | name: "chat",
27 | component: ChatIndex,
28 | },
29 | {
30 | path: "friend",
31 | name: "friend",
32 | component: FriendIndex,
33 | },
34 | {
35 | path: "discover",
36 | name: "discover",
37 | component: DiscoverIndex,
38 | },
39 | {
40 | path: "me",
41 | name: "me",
42 | component: MeIndex,
43 | },
44 | ],
45 | },
46 | {
47 | path: "/chat/message/:to_user/:is_group",
48 | name: "chat-message",
49 | component: ChatMessage,
50 | },
51 | {
52 | path: "/friend/info",
53 | name: "friend-info",
54 | component: FriendInfo,
55 | },
56 | {
57 | path: "/friend/:id/moment",
58 | name: "friend-moment",
59 | component: FriendMoment,
60 | },
61 | {
62 | path: "/friend/apply",
63 | name: "friend-apply",
64 | component: FriendApply,
65 | },
66 | {
67 | path: "/discover/moment",
68 | name: "discover-moment",
69 | component: DiscoverMoment,
70 | },
71 | {
72 | path: "/me/qrcode/index",
73 | name: "me-qrcode-index",
74 | component: QrcodeIndex,
75 | },
76 | {
77 | path: "/me/qrcode/scan",
78 | name: "me-qrcode-scan",
79 | component: QrcodeScan,
80 | },
81 | {
82 | path: "/register",
83 | name: "register",
84 | component: Register,
85 | },
86 | {
87 | path: "/login",
88 | name: "login",
89 | component: Login,
90 | },
91 | ],
92 | });
93 |
94 | export default router;
95 |
--------------------------------------------------------------------------------
/src/components/friend/add.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
26 |
34 |
35 |
36 |
37 |
42 |
43 |
51 | 我的微信号:phpdalao
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 | 面对面建群
65 | 与身边的朋友进入同一个群聊
66 |
67 |
68 |
69 |
70 |
71 |
77 |
78 | 扫一扫
79 | 扫描二维码名片
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/components/friend/perm.vue:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
40 |
41 |
42 |
43 |
44 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
54 |
55 |
56 |
58 |
59 |
60 |
61 |
62 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/views/Login.vue:
--------------------------------------------------------------------------------
1 |
66 |
67 |
68 |
69 | 手机号登录
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | 登录
78 |
79 |
80 | 没有账号?注册
81 |
82 |
83 |
84 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/src/components/chat/group/users.vue:
--------------------------------------------------------------------------------
1 |
52 |
53 |
54 |
55 |
67 |
68 |
69 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
91 |
--------------------------------------------------------------------------------
/src/utils/helper.js:
--------------------------------------------------------------------------------
1 | import { showFailToast, showSuccessToast } from "vant";
2 |
3 | function zeroize(num) {
4 | return (String(num).length == 1 ? "0" : "") + num;
5 | }
6 |
7 | //封装响应
8 | export const handleResponse = (res, cb, router, showSucces = false) => {
9 | console.log(res);
10 | if (res.code != 200001) {
11 | showFailToast(res.msg);
12 | router.go(-1);
13 | return false;
14 | }
15 | if (showSucces) showSuccessToast(res.msg);
16 | cb();
17 | };
18 |
19 | export const timestampFormat = (timestamp, hi = false) => {
20 |
21 | let curTimestamp = parseInt(Date.now() / 1000); //当前时间戳
22 | let timestampDiff = curTimestamp - timestamp; // 参数时间戳与当前时间戳相差秒数
23 |
24 | let curDate = new Date(curTimestamp * 1000); // 当前时间日期对象
25 | let tmDate = new Date(timestamp * 1000); // 参数时间戳转换成的日期对象
26 |
27 | let Y = tmDate.getFullYear(),
28 | m = tmDate.getMonth() + 1,
29 | d = tmDate.getDate();
30 | let H = tmDate.getHours(),
31 | i = tmDate.getMinutes(),
32 | s = tmDate.getSeconds();
33 |
34 | if (timestampDiff < 60) {
35 | // 一分钟以内
36 | return "刚刚";
37 | } else if (timestampDiff < 3600) {
38 | // 一小时前之内
39 | return Math.floor(timestampDiff / 60) + "分钟前";
40 | } else if (
41 | curDate.getFullYear() == Y &&
42 | curDate.getMonth() + 1 == m &&
43 | curDate.getDate() == d
44 | ) {
45 | return zeroize(H) + ":" + zeroize(i);
46 | } else {
47 | let newDate = new Date((curTimestamp - 86400) * 1000); // 参数中的时间戳加一天转换成的日期对象
48 | if (
49 | newDate.getFullYear() == Y &&
50 | newDate.getMonth() + 1 == m &&
51 | newDate.getDate() == d
52 | ) {
53 | return "昨天 " + zeroize(H) + ":" + zeroize(i);
54 | } else if (curDate.getFullYear() == Y) {
55 | return m + "月" + d + "日" + (hi ? zeroize(H) + ":" + zeroize(i) : '');
56 | } else {
57 | return Y + "年" + m + "月" + d + "日" + (hi ? zeroize(H) + ":" + zeroize(i) : '');
58 | }
59 | }
60 | };
61 |
62 | export const timestampFormatMoment = (timestamp) => {
63 |
64 | let curTimestamp = parseInt(Date.now() / 1000); //当前时间戳
65 | let timestampDiff = curTimestamp - timestamp; // 参数时间戳与当前时间戳相差秒数
66 |
67 | let tmDate = new Date(timestamp * 1000); // 参数时间戳转换成的日期对象
68 |
69 | let m = tmDate.getMonth() + 1,
70 | d = tmDate.getDate();
71 |
72 | if (timestampDiff < 86400)
73 | return "今天";
74 | if (timestampDiff < 86400 * 2)
75 | return "昨天";
76 | if (timestampDiff < 86400 * 3)
77 | return "前天";
78 | return "" + zeroize(d) + " " + m + "月";
79 | };
80 |
81 | export const durationFormat = (duration) => {
82 | let h = Math.floor(duration / 3600);
83 | let m = Math.floor((duration - h * 3600) / 60);
84 | let s = duration - h * 3600 - m * 60;
85 | return zeroize(h) + ":" + zeroize(m) + ":" + zeroize(s);
86 | }
87 |
--------------------------------------------------------------------------------
/src/components/chat/friend/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
49 |
50 |
51 |
52 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
66 |
67 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/src/components/me/wallet.vue:
--------------------------------------------------------------------------------
1 |
48 |
49 |
50 |
51 |
52 | {}"
53 | :border="false"/>
54 |
55 |
56 |
57 |
58 | 我的零钱
59 |
¥{{ (userStore.info.money / 100).toFixed(2) }}
60 |
61 | 充值金额
62 | ¥{{ (money * 1).toFixed(2) }}
63 | 充值
64 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/views/me/qrcode/scan.vue:
--------------------------------------------------------------------------------
1 |
81 |
82 |
83 |
85 |
86 |
87 |
88 |
90 |
91 |
93 |
94 |
95 |
96 |
107 |
--------------------------------------------------------------------------------
/src/components/friend/remark.vue:
--------------------------------------------------------------------------------
1 |
2 |
40 |
41 |
47 |
48 |
54 |
55 | 完成
56 |
57 |
58 |
59 |
93 |
94 |
95 |
96 |
113 |
--------------------------------------------------------------------------------
/src/components/chat/redPacket/record.vue:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
29 |
“{{ props.redPacket?.from.nickname }}”的红包
30 | ¥{{ (props.redPacket?.money/100).toFixed(2) }}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/views/friend/index.vue:
--------------------------------------------------------------------------------
1 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
62 |
63 |
64 |
66 |
67 |
68 |
69 |
70 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/src/stores/app.js:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 | import { defineStore } from "pinia";
3 | import { Home } from "@/enums/app";
4 | import { UnreadChat, UnreadApply, UnreadMoment, ThemeIcon } from "@/enums/app";
5 | export const useAppStore = defineStore(
6 | "app",
7 | () => {
8 | const theme = ref("light");
9 | const icon = ref(ThemeIcon.light);
10 | const showSearch = ref(true);
11 | const showNavbar = ref(true);
12 | const navTitle = ref("");
13 | const showCommonSearch = ref(false);
14 | const commonSearchAction = ref(Home);
15 | const commonSearchPlaceholder = ref("搜索");
16 | const defaultUnread = {
17 | chat: 0,
18 | apply: 0,
19 | discover: 0,
20 | moment: {
21 | num: 0,
22 | from: {}
23 | },
24 | friend: 0
25 | };
26 | const unread = ref(defaultUnread);
27 |
28 | const initCommonSearch = (search) => {
29 | commonSearchAction.value = search.action;
30 | commonSearchPlaceholder.value = search.placeholder;
31 | };
32 |
33 | const initHeader = (header) => {
34 | navTitle.value = header.title;
35 | showNavbar.value = header.navbar;
36 | showSearch.value = header.search;
37 | };
38 |
39 | const setShowCommonSearch = (show) => {
40 | showCommonSearch.value = show;
41 | };
42 |
43 | const unreadIncrBy = (unreadType, incr = 1, from = {}) => {
44 | switch (unreadType) {
45 | case UnreadChat:
46 | unread.value.chat += incr;
47 | break;
48 | case UnreadMoment:
49 | unread.value.moment.num += incr;
50 | unread.value.moment.from = from;
51 | unread.value.discover += incr;
52 | break;
53 | case UnreadApply:
54 | unread.value.apply += incr;
55 | unread.value.friend += incr;
56 | break;
57 |
58 | }
59 | };
60 |
61 | const unreadDecrBy = (unreadType, decr = 1) => {
62 | switch (unreadType) {
63 | case UnreadChat:
64 | unread.value.chat -= decr;
65 | if (unread.value.chat < 0) unread.value.chat = 0;
66 | break;
67 | case UnreadApply:
68 | unread.value.apply -= decr;
69 | if (unread.value.apply < 0) unread.value.apply = 0;
70 | unread.value.friend -= decr;
71 | if (unread.value.friend < 0) unread.value.friend = 0;
72 | break;
73 | }
74 | };
75 |
76 | const clearMomentUnread = () => {
77 | unread.value.moment.num = 0;
78 | unread.value.moment.from = {};
79 | unread.value.discover = 0;
80 | };
81 |
82 | const setUnread = (data) => {
83 | unread.value = data;
84 | };
85 |
86 | const clear = () => {
87 | unread.value = defaultUnread;
88 | showNavbar.value = true;
89 | showSearch.value = true;
90 | navTitle.value = "";
91 | showCommonSearch.value = false;
92 | commonSearchAction.value = Home;
93 | commonSearchPlaceholder.value = "搜索";
94 | };
95 |
96 | const setTheme = (t) => {
97 | theme.value = t;
98 | icon.value = ThemeIcon[t];
99 | };
100 | return {
101 | showNavbar,
102 | showSearch,
103 | navTitle,
104 | showCommonSearch,
105 | commonSearchAction,
106 | commonSearchPlaceholder,
107 | unread,
108 | theme,
109 | icon,
110 | initHeader,
111 | setShowCommonSearch,
112 | initCommonSearch,
113 | unreadIncrBy,
114 | unreadDecrBy,
115 | clearMomentUnread,
116 | setUnread,
117 | clear,
118 | setTheme,
119 | };
120 | },
121 | { persist: true }
122 | );
123 |
--------------------------------------------------------------------------------
/src/views/Register.vue:
--------------------------------------------------------------------------------
1 |
67 |
68 |
69 |
70 | 手机号注册
71 |
80 |
81 |
82 |
88 |
94 |
100 |
107 |
108 |
109 |
110 | 注册
111 |
112 |
113 | 已有账号?登录
114 |
115 |
116 |
117 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/src/components/common/background.vue:
--------------------------------------------------------------------------------
1 |
2 |
88 |
89 |
90 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
118 |
--------------------------------------------------------------------------------
/src/components/chat/group/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
38 |
39 |
40 |
46 |
54 |
55 |
56 |
57 |
62 |
63 |
64 |
65 |
75 |
76 |
77 |
![avatar]()
83 |
84 |
85 |
86 | {{ item.nickname }}
87 |
88 |
89 |
90 |
91 |
{{ groupList.length }}个群聊
92 |
93 |
94 |
95 |
96 |
97 |
139 |
--------------------------------------------------------------------------------
/src/assets/theme.css:
--------------------------------------------------------------------------------
1 | /* 主题通用 */
2 | :root:root {
3 | --theme-primary-color: #57be6a;
4 | --theme-danger-color: #f56c6c;
5 | --theme-blue-1970: #5b6a91;
6 | --theme-black: #000;
7 | --theme-black-11: #111111;
8 | --theme-black-18: #181818;
9 | --theme-black-1c: #1c1c1c;
10 | --theme-black-1e: #1e1e1e;
11 | --theme-black-19: #191919;
12 | --theme-black-20: #202020;
13 | --theme-black-21: #212121;
14 | --theme-black-28: #282828;
15 | --theme-black-4c: #4c4c4c;
16 | --theme-white: #fff;
17 | --theme-white-bc: #bcbcbc;
18 | --theme-white-cd: #cdcdcd;
19 | --theme-white-de: #dedede;
20 | --theme-white-ed: #ededed;
21 | --theme-white-f5: #f5f5f5;
22 | --theme-white-f6: #f6f6f6;
23 | --theme-white-f7: #f7f7f7;
24 | --theme-gray-70: #707070;
25 | --theme-red: red;
26 | --van-tabbar-height: 64px;
27 | --van-navbar-height: 46px;
28 | --van-tabbar-item-active-color: var(--theme-primary-color);
29 | --van-primary-color: var(--theme-primary-color);
30 | }
31 |
32 | /* 普通主题 */
33 | .van-theme-light {
34 | .header-bottom-border{
35 | border-bottom: 1px solid var(--theme-white-de);
36 | }
37 | .container {
38 | background: var(--theme-white-ed);
39 | }
40 | .bg-white {
41 | background: var(--theme-white);
42 | }
43 | .top-active {
44 | background: var(--theme-white-ed);
45 | }
46 | --black-white-color: var(--theme-black);
47 | --black4c-whitebc-color: var(--theme-black-4c);
48 | --black19-white-color: var(--theme-white);
49 | --black20-white-color: var(--theme-white);
50 | --black20-whitef7-color: var(--theme-white-f7);
51 | --van-nav-bar-background: var(--theme-white-ed);
52 | --van-nav-bar-icon-color: var(--theme-black-18);
53 | --van-nav-bar-text-color: var(--theme-black-18);
54 | --common-search-background: var(--theme-white);
55 | --messge-footer-background: var(--theme-white-f6);
56 | --messge-footer-input-background: var(--theme-white);
57 | --friend-info-van-nav-bar: var(--theme-white);
58 | --friend-add-qrcode-text: var(--theme-black);
59 | --friend-remark-nav-bar-background: var(--theme-white);
60 | --friend-remark-background: var(--theme-white);
61 | --friend-remark-from-input-background: var(--theme-white-f7);
62 | --van-search-background: var(--van-nav-bar-background);
63 | --van-search-content-background: var(--theme-white);
64 | --van-tabbar-background: var(--theme-white-f5);
65 | --van-tabbar-item-active-background: var(--theme-white-f5);
66 | }
67 |
68 | /* 暗黑主题 */
69 | .van-theme-dark {
70 | .header-bottom-border{
71 | border-bottom: 1px solid var(--theme-black);
72 | }
73 | .container,
74 | .bg-white {
75 | background: var(--theme-black-11);
76 | }
77 | .group-update .van-cell-group,
78 | .group-update .van-cell {
79 | background: transparent;
80 | }
81 | .top-active {
82 | background: var(--theme-black-21);
83 | }
84 | --black-white-color: var(--theme-white);
85 | --black4c-whitebc-color: var(--theme-white-bc);
86 | --black19-white-color: var(--theme-black-19);
87 | --black20-white-color: var(--theme-black-20);
88 | --black20-whitef7-color: var(--theme-black-20);
89 | --van-nav-bar-background: var(--theme-black-11);
90 | --van-nav-bar-icon-color: var(--theme-white);
91 | --van-nav-bar-text-color: var(--theme-white);
92 | --common-search-background: var(--theme-black-18);
93 | --messge-footer-background: var(--theme-black-1c);
94 | --messge-footer-input-background: var(--theme-black-28);
95 | --friend-info-van-nav-bar: var(--theme-black-1c);
96 | --friend-add-qrcode-text: var(--theme-white);
97 | --friend-remark-nav-bar-background: var(--theme-black-11);
98 | --friend-remark-background: var(--theme-black-11);
99 | --friend-remark-from-input-background: var(--theme-black-1e);
100 | --van-search-background: var(--van-nav-bar-background);
101 | --van-search-content-background: var(--theme-black-18);
102 | --van-tabbar-background: var(--theme-black-1e);
103 | --van-tabbar-item-active-background: var(--theme-black-1e);
104 | --van-cell-group-background: var(--theme-black-18);
105 | }
106 |
--------------------------------------------------------------------------------
/src/components/discover/moment/post.vue:
--------------------------------------------------------------------------------
1 |
70 |
71 |
72 |
73 |
74 |
75 | 发表
76 |
77 |
78 |
79 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/src/components/me/setting.vue:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
67 |
68 |
69 |
70 |
71 |
73 |
74 |
75 |
76 |
77 |
79 |
80 |
81 |
82 |
83 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
98 |
--------------------------------------------------------------------------------
/src/components/chat/group/update.vue:
--------------------------------------------------------------------------------
1 |
94 |
95 |
96 |
99 |
100 |
102 |
103 |
{{ text[props.updateField].h1 }}
104 |
{{ text[props.updateField].h4 }}
105 |
106 |
107 |
108 |
109 |
![avatar]()
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
确定
120 |
121 |
122 |
123 |
124 |
161 |
--------------------------------------------------------------------------------
/src/components/friend/new.vue:
--------------------------------------------------------------------------------
1 |
2 |
69 |
70 |
71 |
72 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | {{ item.friend.nickname }}
88 | {{ item.remark }}
89 |
90 |
91 |
92 |
93 | {{ ApplyStatus[item.status] }}
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | {{ item.friend.nickname }}
109 | {{ item.remark }}
110 |
111 |
112 |
113 |
114 | {{ ApplyStatus[item.status] }}
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
133 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | 登录
107 | 注册
108 |
109 |
110 |
111 |
112 |
135 |
--------------------------------------------------------------------------------
/src/views/me/qrcode/index.vue:
--------------------------------------------------------------------------------
1 |
49 |
50 |
51 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | {{ userStore.info.nickname }}
71 | 英国 贝尔法斯特
72 |
73 |
74 |
75 |
76 |
扫一扫上面的二维码图案,加我为朋友
77 |
78 |
79 |
80 |
86 |
87 |
88 |
181 |
--------------------------------------------------------------------------------
/src/views/me/index.vue:
--------------------------------------------------------------------------------
1 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | {{ userStore.info.nickname
67 | }}
68 | 微信号:{{ userStore.info.wechat }}
69 | 地区:广州
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | 深夜模式
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | 钱包
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | 设置
109 |
110 |
111 |
112 |
113 |
114 |
吴彦祖,给个star呗!
116 |
退出登录
118 |
119 |
120 |
121 |
122 |
123 |
124 |
162 |
--------------------------------------------------------------------------------
/src/components/common/search.vue:
--------------------------------------------------------------------------------
1 |
2 |
54 |
55 |
56 |
61 |
66 |
68 |
69 |
70 |
71 |
72 |
74 |
75 |
76 | {{ item.nickname }}
77 |
78 |
79 |
80 |
81 | 用户不存在
82 |
83 |
84 |
85 |
86 |
87 | 搜索:{{ keywords }}
89 |
90 |
91 |
92 |
93 |
94 |
98 |
99 |
100 | 快速搜索聊天内容
101 |
102 |
103 | 日期
104 | 图片与视频
110 | 文件
111 |
112 |
113 | 链接
114 | 音乐与音频
120 | 交易
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
146 |
--------------------------------------------------------------------------------
/src/views/friend/apply.vue:
--------------------------------------------------------------------------------
1 |
80 |
81 |
82 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
102 |
103 |
104 |
105 |
106 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
121 |
122 |
123 |
124 |
125 |
127 |
128 |
129 |
130 |
131 |
132 | {{ formData.type == Apply ? "提交" : "完成" }}
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
164 |
--------------------------------------------------------------------------------
/src/components/chat/list.vue:
--------------------------------------------------------------------------------
1 |
40 |
41 |
42 |
43 |
50 |
51 |
53 |
54 |
![avatar]()
55 |
56 |
57 |
58 |
59 | {{ item.nickname }}
60 | {{ item.content }}
61 |
62 |
63 | {{ timestampFormat(item.time) }}
64 |
65 |
66 |
67 |
68 |
69 |
70 | 0 ? 0 : 1)" />
72 |
73 |
74 |
75 |
76 |
77 |
205 |
--------------------------------------------------------------------------------
/src/components/chat/redPacket/detail.vue:
--------------------------------------------------------------------------------
1 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | {{ props.redPacket?.from.nickname }}的红包
72 |
73 |
74 |
75 |
{{ message }}
76 | 查看红包记录
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
175 |
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/src/components/chat/group/action.vue:
--------------------------------------------------------------------------------
1 |
2 |
120 |
121 |
122 |
123 |
124 |
125 | 完成
126 |
127 |
128 |
129 |
130 |
131 |
132 |
0" @update:model-value="searchResult" />
134 |
135 |
136 |
137 |
138 |
139 |
140 |
142 |
143 | {
145 | friendList[val][index].checked = checked;
146 | }
147 | ">
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
181 |
--------------------------------------------------------------------------------
/src/components/discover/moment/message.vue:
--------------------------------------------------------------------------------
1 |
74 |
75 |
76 |
77 |
80 |
81 |
82 |
84 |
86 |
87 |
89 |
90 |
91 |
92 | {{ item.from.nickname }}
93 |
94 |
95 |
100 |
101 |
102 |
103 |
104 |
{{ item.created_at }}
105 |
106 |
107 |
108 |
110 |
111 |
112 |
113 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/src/components/chat/info.vue:
--------------------------------------------------------------------------------
1 |
2 |
88 |
89 |
90 |
91 |
94 |
95 |
96 |
97 |
98 |
99 |
101 |
102 |
103 |
104 |
105 |
107 |
108 |
110 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
133 |
134 |
135 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
151 |
152 |
153 |
154 |
156 |
158 |
160 |
161 |
174 |
--------------------------------------------------------------------------------
/src/views/friend/info.vue:
--------------------------------------------------------------------------------
1 |
62 |
63 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
81 |
82 | {{ homeInfo.display_nickname
83 | }}
84 | 昵称:{{ homeInfo.nickname
85 | }}
86 | 微信号:{{ homeInfo.wechat }}
87 | 地区:上海
88 |
89 |
90 |
91 | {{ homeInfo.check_msg }}
92 |
93 |
94 |
95 |
96 |
98 |
100 |
101 |
102 |
103 |
104 | 朋友圈
105 |
107 |
108 |
109 |
110 |
111 |
112 |
114 |
115 |
发送消息
119 |
添加到通讯录
123 |
前往验证
125 |
126 |
127 |
128 |
129 |
130 |
131 |
133 |
134 |
196 |
197 |
--------------------------------------------------------------------------------
/src/components/chat/redPacket/form.vue:
--------------------------------------------------------------------------------
1 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
181 |
183 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/src/components/Verifition/src/Verify/VerifyPoints.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
19 |
20 |
21 |
![]()
28 |
29 |
47 | {{ index + 1 }}
48 |
49 |
50 |
51 |
52 |
61 | {{ text }}
62 |
63 |
64 |
65 |
265 |
--------------------------------------------------------------------------------