├── .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 | -------------------------------------------------------------------------------- /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 | 40 | -------------------------------------------------------------------------------- /src/views/chat/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 39 | 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 | 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 | 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 | 89 | -------------------------------------------------------------------------------- /src/components/friend/perm.vue: -------------------------------------------------------------------------------- 1 | 28 | 71 | 72 | -------------------------------------------------------------------------------- /src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 88 | -------------------------------------------------------------------------------- /src/components/chat/group/users.vue: -------------------------------------------------------------------------------- 1 | 52 | 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 | 78 | -------------------------------------------------------------------------------- /src/components/me/wallet.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 74 | 75 | -------------------------------------------------------------------------------- /src/views/me/qrcode/scan.vue: -------------------------------------------------------------------------------- 1 | 81 | 96 | 107 | -------------------------------------------------------------------------------- /src/components/friend/remark.vue: -------------------------------------------------------------------------------- 1 | 2 | 40 | 95 | 96 | 113 | -------------------------------------------------------------------------------- /src/components/chat/redPacket/record.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 36 | 37 | -------------------------------------------------------------------------------- /src/views/friend/index.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 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 | 126 | -------------------------------------------------------------------------------- /src/components/common/background.vue: -------------------------------------------------------------------------------- 1 | 2 | 88 | 111 | 118 | -------------------------------------------------------------------------------- /src/components/chat/group/list.vue: -------------------------------------------------------------------------------- 1 | 2 | 38 | 39 | 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 | 94 | -------------------------------------------------------------------------------- /src/components/me/setting.vue: -------------------------------------------------------------------------------- 1 | 2 | 26 | 27 | 96 | 98 | -------------------------------------------------------------------------------- /src/components/chat/group/update.vue: -------------------------------------------------------------------------------- 1 | 94 | 124 | 161 | -------------------------------------------------------------------------------- /src/components/friend/new.vue: -------------------------------------------------------------------------------- 1 | 2 | 69 | 70 | 130 | 133 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 111 | 112 | 135 | -------------------------------------------------------------------------------- /src/views/me/qrcode/index.vue: -------------------------------------------------------------------------------- 1 | 49 | 88 | 181 | -------------------------------------------------------------------------------- /src/views/me/index.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 123 | 124 | 162 | -------------------------------------------------------------------------------- /src/components/common/search.vue: -------------------------------------------------------------------------------- 1 | 2 | 54 | 55 | 130 | 146 | -------------------------------------------------------------------------------- /src/views/friend/apply.vue: -------------------------------------------------------------------------------- 1 | 80 | 141 | 164 | -------------------------------------------------------------------------------- /src/components/chat/list.vue: -------------------------------------------------------------------------------- 1 | 40 | 76 | 77 | 205 | -------------------------------------------------------------------------------- /src/components/chat/redPacket/detail.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 86 | 87 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 161 | 162 | 180 | -------------------------------------------------------------------------------- /src/components/chat/group/action.vue: -------------------------------------------------------------------------------- 1 | 2 | 120 | 170 | 171 | 181 | -------------------------------------------------------------------------------- /src/components/discover/moment/message.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 129 | 130 | -------------------------------------------------------------------------------- /src/components/chat/info.vue: -------------------------------------------------------------------------------- 1 | 2 | 88 | 89 | 161 | 174 | -------------------------------------------------------------------------------- /src/views/friend/info.vue: -------------------------------------------------------------------------------- 1 | 62 | 134 | 196 | 197 | -------------------------------------------------------------------------------- /src/components/chat/redPacket/form.vue: -------------------------------------------------------------------------------- 1 | 89 | 90 | 187 | 188 | -------------------------------------------------------------------------------- /src/components/Verifition/src/Verify/VerifyPoints.vue: -------------------------------------------------------------------------------- 1 | 65 | 265 | --------------------------------------------------------------------------------