私域操盘手模块调整
@@ -1,8 +0,0 @@
|
||||
NODE_ENV=production
|
||||
VUE_APP_PREVIEW=false
|
||||
VUE_APP_API_BASE_URL=http://yishi.com
|
||||
VUE_APP_WWW_BASE_URL=http://yishi.com
|
||||
VUE_APP_WEB_SOCKET_URL=ws://yishi.com:2348
|
||||
VUE_APP_WEBSITE_NAME="管理后台"
|
||||
VUE_APP_TITLE=医师管理系统
|
||||
VUE_APP_API_URL=http://yishi.com
|
||||
@@ -1,14 +0,0 @@
|
||||
# 环境标识
|
||||
NODE_ENV=production
|
||||
|
||||
# 应用名称
|
||||
VUE_APP_WEBSITE_NAME=医师管理系统
|
||||
|
||||
# API基础URL
|
||||
VUE_APP_API_BASE_URL=https://api.yishi.com
|
||||
|
||||
# 前端网站URL
|
||||
VUE_APP_WWW_BASE_URL=https://www.yishi.com
|
||||
|
||||
# WebSocket URL
|
||||
VUE_APP_WEB_SOCKET_URL=wss://api.yishi.com/ws
|
||||
@@ -1,14 +0,0 @@
|
||||
# 环境标识
|
||||
NODE_ENV=production
|
||||
|
||||
# 应用名称
|
||||
VUE_APP_WEBSITE_NAME=医师管理系统(测试)
|
||||
|
||||
# API基础URL
|
||||
VUE_APP_API_BASE_URL=https://test-api.yishi.com
|
||||
|
||||
# 前端网站URL
|
||||
VUE_APP_WWW_BASE_URL=https://test.yishi.com
|
||||
|
||||
# WebSocket URL
|
||||
VUE_APP_WEB_SOCKET_URL=wss://test-api.yishi.com/ws
|
||||
24
Backend/.gitignore
vendored
@@ -1,24 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 YuanDong
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,166 +0,0 @@
|
||||
# Lumen IM 即时聊天系统(前端)
|
||||
|
||||
### 项目介绍
|
||||
Lumen IM 是一个网页版在线即时聊天项目,前端使用 Element-ui + Vue,后端采用了基于 Swoole 开发的 Hyperf 协程框架进行接口开发,并使用 WebSocket 服务进行消息实时推送。目前后端 WebSocket 已支持分布式集群部署。
|
||||
|
||||
目前该项目是在 [旧版本](https://github.com/gzydong/LumenIM/tree/v1.0.0) 项目的基础上进行了后端重构,且前后端都有较大的改动。
|
||||
|
||||
### 功能模块
|
||||
- 基于 Swoole WebSocket 服务做消息即时推送
|
||||
- 支持私聊及群聊
|
||||
- 支持多种聊天消息类型 例如:文本、代码块、图片及其它类型文件,并支持文件下载
|
||||
- 支持聊天消息撤回、删除(批量删除)、转发消息(逐条转发、合并转发)
|
||||
- 支持编写个人笔记、支持笔记分享(好友或群)
|
||||
|
||||
### 项目预览
|
||||
- 地址: [http://im.gzydong.club](http://im.gzydong.club)
|
||||
- 账号: 18798272054 或 18798272055
|
||||
- 密码: admin123
|
||||
|
||||
### 项目安装(部署)
|
||||
###### 设置 npm 镜像源
|
||||
```language
|
||||
npm config set registry https://registry.npm.taobao.org
|
||||
```
|
||||
|
||||
###### 下载安装
|
||||
```bash
|
||||
## 克隆项目源码包
|
||||
git clone git@github.com:gzydong/LumenIM.git
|
||||
|
||||
## 安装项目依赖扩展组件
|
||||
npm install
|
||||
|
||||
# 启动本地开发环境
|
||||
npm run serve
|
||||
|
||||
## 生产环境构建项目
|
||||
npm run build
|
||||
|
||||
## 生产环境构建项目并查看构建报告
|
||||
npm run build --report
|
||||
```
|
||||
|
||||
###### 修改 .env 配置信息
|
||||
|
||||
```env
|
||||
VUE_APP_API_BASE_URL=http://xxx.yourdomain.com
|
||||
VUE_APP_WEB_SOCKET_URL=ws://xxx.yourdomain.com/socket.io
|
||||
VUE_APP_WEBSITE_NAME="Lumen IM"
|
||||
```
|
||||
|
||||
###### 关于 Nginx 的一些配置
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name www.yourdomain.com;
|
||||
|
||||
root /project-path/dist;
|
||||
index index.html;
|
||||
|
||||
## 解决 VueRouter History 模式下 页面刷新404问题
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|ico)$ {
|
||||
expires 7d;
|
||||
}
|
||||
|
||||
location ~ .*\.(js|css)?$ {
|
||||
expires 7d;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意:项目需要与后端一起使用,[点击获取源码](https://github.com/gzydong/hyperf-chat)。
|
||||
|
||||
### 项目源码
|
||||
|代码仓库|前端源码|后端源码|
|
||||
|-|-|-|
|
||||
|Github|https://github.com/gzydong/LumenIM|https://github.com/gzydong/hyperf-chat|
|
||||
|码云|https://gitee.com/gzydong/LumenIM||
|
||||
|
||||
|
||||
#### 联系方式
|
||||
QQ : 837215079
|
||||
|
||||
### 如果你觉得还不错,请 Star , Fork 给作者鼓励一下。
|
||||
|
||||
## 环境变量配置
|
||||
|
||||
本项目使用Vue CLI的环境变量配置功能,可以在不同环境下使用不同的配置。
|
||||
|
||||
### 配置文件
|
||||
|
||||
- `.env` - 所有环境的默认配置
|
||||
- `.env.development` - 开发环境配置
|
||||
- `.env.test` - 测试环境配置
|
||||
- `.env.production` - 生产环境配置
|
||||
|
||||
### 环境变量
|
||||
|
||||
项目中使用的主要环境变量:
|
||||
|
||||
- `VUE_APP_WEBSITE_NAME` - 网站名称
|
||||
- `VUE_APP_API_BASE_URL` - API基础URL
|
||||
- `VUE_APP_WWW_BASE_URL` - 前端网站URL
|
||||
- `VUE_APP_WEB_SOCKET_URL` - WebSocket URL
|
||||
|
||||
### 使用方法
|
||||
|
||||
在代码中可以通过以下方式访问环境变量:
|
||||
|
||||
```js
|
||||
// 直接访问
|
||||
console.log(process.env.VUE_APP_API_BASE_URL)
|
||||
|
||||
// 通过配置文件访问
|
||||
import config from '@/config/config'
|
||||
console.log(config.BASE_API_URL)
|
||||
```
|
||||
|
||||
### 运行与构建
|
||||
|
||||
开发环境:
|
||||
```bash
|
||||
# 使用开发环境配置运行
|
||||
npm run serve:dev
|
||||
|
||||
# 使用开发环境配置构建
|
||||
npm run build:dev
|
||||
```
|
||||
|
||||
测试环境:
|
||||
```bash
|
||||
# 使用测试环境配置运行
|
||||
npm run serve:test
|
||||
|
||||
# 使用测试环境配置构建
|
||||
npm run build:test
|
||||
```
|
||||
|
||||
生产环境:
|
||||
```bash
|
||||
# 使用生产环境配置运行
|
||||
npm run serve:prod
|
||||
|
||||
# 使用生产环境配置构建
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
## 项目设置
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 启动开发服务器
|
||||
npm run serve
|
||||
|
||||
# 构建生产版本
|
||||
npm run build
|
||||
|
||||
# 代码检查
|
||||
npm run lint
|
||||
```
|
||||
@@ -1,51 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
"import",
|
||||
{
|
||||
"libraryName": "element-ui",
|
||||
"styleLibraryName": "theme-chalk"
|
||||
}
|
||||
],
|
||||
[
|
||||
"prismjs",
|
||||
{
|
||||
"languages": [
|
||||
"html",
|
||||
"css",
|
||||
"less",
|
||||
"javascript",
|
||||
"typescript",
|
||||
"json",
|
||||
"xml",
|
||||
"bash",
|
||||
"nginx",
|
||||
"sql",
|
||||
"docker",
|
||||
"php",
|
||||
"java",
|
||||
"go",
|
||||
"python",
|
||||
"ruby",
|
||||
"rust",
|
||||
"objectivec",
|
||||
"c",
|
||||
"csharp",
|
||||
"cpp",
|
||||
"lua",
|
||||
"shell",
|
||||
"vim",
|
||||
"yaml",
|
||||
"yml",
|
||||
"md",
|
||||
"erlang",
|
||||
"ini"
|
||||
],
|
||||
"theme": "okaidia"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
19857
Backend/package-lock.json
generated
@@ -1,73 +0,0 @@
|
||||
{
|
||||
"name": "yishi-admin",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"serve:dev": "vue-cli-service serve --mode development",
|
||||
"serve:test": "vue-cli-service serve --mode test",
|
||||
"serve:prod": "vue-cli-service serve --mode production",
|
||||
"build": "vue-cli-service build",
|
||||
"build:dev": "vue-cli-service build --mode development",
|
||||
"build:test": "vue-cli-service build --mode test",
|
||||
"build:prod": "vue-cli-service build --mode production",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"babel-plugin-prismjs": "^2.0.1",
|
||||
"core-js": "^3.6.5",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-ui": "^2.15.6",
|
||||
"js-audio-recorder": "^1.0.6",
|
||||
"js-base64": "^2.5.1",
|
||||
"mavon-editor": "^2.9.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"prismjs": "^1.22.0",
|
||||
"svg-sprite-loader": "^5.0.0",
|
||||
"vue": "^2.6.11",
|
||||
"vue-contextmenujs": "^1.3.13",
|
||||
"vue-cropper": "^0.5.5",
|
||||
"vue-prism-editor": "^0.5.1",
|
||||
"vue-router": "^3.2.0",
|
||||
"vuex": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-plugin-import": "^1.13.1",
|
||||
"compression-webpack-plugin": "^6.1.1",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"less": "^3.0.4",
|
||||
"less-loader": "^5.0.0",
|
||||
"style-resources-loader": "^1.4.1",
|
||||
"vue-cli-plugin-style-resources-loader": "^0.1.4",
|
||||
"vue-svg-component-runtime": "^1.0.1",
|
||||
"vue-svg-icon-loader": "^2.1.1",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,34 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cmn-Hans">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<title>Lumen IM</title>
|
||||
<style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
|
||||
<!-- require cdn assets css -->
|
||||
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
|
||||
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
|
||||
<% } %>
|
||||
<link rel="stylesheet" href="https://at.alicdn.com/t/font_1425251_3v0kq1by4iq.css">
|
||||
<link rel="stylesheet" href="https://cdn.bootcss.com/animate.css/3.7.2/animate.css">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app">
|
||||
<div class="first-loading-wrp">
|
||||
<div class="loading-wrp">
|
||||
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- require cdn assets js -->
|
||||
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
|
||||
<script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
|
||||
<% } %>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,130 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import config from '@/config/config'
|
||||
|
||||
var uuid = () => {
|
||||
var s = [];
|
||||
var hex = '0123456789abcdef'
|
||||
for (var i = 0; i < 36; i++) {
|
||||
s[i] = hex.substr(Math.floor(Math.random() * 0x10), 1)
|
||||
}
|
||||
s[14] = '4' // bits 12-15 of the time_hi_and_version field to 0010
|
||||
s[19] = hex.substr((s[19] & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
|
||||
s[8] = s[13] = s[18] = s[23] = '-'
|
||||
|
||||
return s.join('');
|
||||
}
|
||||
|
||||
var log = (message, contexts = {}, ex = null) => {
|
||||
if (config.WS_DEBUG) {
|
||||
if (typeof contexts === 'object') {
|
||||
for (var k in contexts) {
|
||||
message = message.replaceAll('{' + k + '}', typeof contexts[k] === 'object' ? JSON.stringify(contexts[k]) : contexts[k])
|
||||
}
|
||||
}
|
||||
if (ex) {
|
||||
console.log('[WebSocket] ' + message, ex)
|
||||
} else {
|
||||
console.log('[WebSocket] ' + message)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var callbacks = {}
|
||||
var reqQueues = []
|
||||
var connected = false
|
||||
var websocket = null
|
||||
var logininfo = {}
|
||||
|
||||
Vue.prototype.$wsConnect = (username, password, callback) => {
|
||||
websocket = new WebSocket(config.WS_URL)
|
||||
websocket.onmessage = (e) => {
|
||||
log('recv: {data}', { data: e.data })
|
||||
try {
|
||||
var data = JSON.parse(e.data)
|
||||
if (data['type'] === 'answer') {
|
||||
if (callbacks[data['requestId']]) {
|
||||
callbacks[data['requestId']](data['data'])
|
||||
delete callbacks[data['requestId']];
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
log('recv error: {data}', { data: e.data }, ex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
websocket.onopen = (e) => {
|
||||
log('open: {e}', { e })
|
||||
|
||||
Vue.prototype.$wsReq('login', {
|
||||
username: username,
|
||||
password: password,
|
||||
}, (ret) => {
|
||||
if (typeof callback === 'function') {
|
||||
callback(ret)
|
||||
}
|
||||
if (ret.ok) {
|
||||
connected = true
|
||||
logininfo = { username, password }
|
||||
if (reqQueues.length > 0) {
|
||||
for (let i = 0; i < reqQueues.length; i ++) {
|
||||
Vue.prototype.$wsSend(reqQueues[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
websocket.close()
|
||||
log('login error: {ret}', { ret })
|
||||
}
|
||||
}, true)
|
||||
}
|
||||
websocket.onclose = (e) => {
|
||||
connected = false
|
||||
|
||||
log('close: {e}', { e })
|
||||
if (logininfo) {
|
||||
log('reconnect in three seconds')
|
||||
setTimeout(() => {
|
||||
Vue.prototype.$wsConnect(logininfo.username, logininfo.password)
|
||||
}, 3000)
|
||||
}
|
||||
}
|
||||
websocket.onerror = (e) => {
|
||||
log('error: {e}', { e })
|
||||
}
|
||||
}
|
||||
|
||||
Vue.prototype.$wsSend = (message, force = false) => {
|
||||
if (connected || force) {
|
||||
websocket.send(message)
|
||||
} else {
|
||||
reqQueues.push(message)
|
||||
}
|
||||
}
|
||||
|
||||
Vue.prototype.$wsReq = (type, data, callback, force = false) => {
|
||||
var reqId = uuid();
|
||||
var message = JSON.stringify(Object.assign(config.WS_REQ_PARAMS, {
|
||||
type: type,
|
||||
requestId: reqId,
|
||||
data: data,
|
||||
}))
|
||||
|
||||
if (typeof callback == 'function') {
|
||||
callbacks[reqId] = callback
|
||||
}
|
||||
|
||||
log('send: {message}', { message })
|
||||
|
||||
Vue.prototype.$wsSend(message, force)
|
||||
}
|
||||
|
||||
Vue.prototype.$wsClose = () => {
|
||||
logininfo = { username: '', password: '' }
|
||||
connected = false
|
||||
websocket.close()
|
||||
}
|
||||
|
||||
/*Vue.prototype.$wsConnect('test', '123456', (ret) => {
|
||||
console.log('haha', ret)
|
||||
})*/
|
||||
@@ -1,48 +0,0 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view v-if="showView" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
data() {
|
||||
return {
|
||||
// 用于点击当前页的router时,刷新当前页
|
||||
showView: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 刷新当前路由方法
|
||||
refreshView() {
|
||||
this.showView = false
|
||||
this.$nextTick(() => (this.showView = true))
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.image-uploader .el-upload {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.image-uploader .el-upload:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
.image-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 118px;
|
||||
height: 118px;
|
||||
line-height: 118px !important;
|
||||
text-align: center;
|
||||
}
|
||||
.image-uploader .image {
|
||||
width: 118px;
|
||||
height: 118px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
@@ -1,130 +0,0 @@
|
||||
import { post, get, upload } from '@/utils/request'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import config from '@/config/config'
|
||||
|
||||
// 查询用户文集分类服务接口
|
||||
export const ServeGetArticleClass = data => {
|
||||
return get('/api/v1/article/article-class', data)
|
||||
}
|
||||
|
||||
// 获取笔记表标签服务接口
|
||||
export const ServeGetArticleTag = data => {
|
||||
return get('/api/v1/article/article-tags', data)
|
||||
}
|
||||
|
||||
// 查询用户文集分类服务接口
|
||||
export const ServeGetArticleList = data => {
|
||||
return get('/api/v1/article/article-list', data)
|
||||
}
|
||||
|
||||
// 查询用户文集分类服务接口
|
||||
export const ServeGetArticleDetail = data => {
|
||||
return get('/api/v1/article/article-detail', data)
|
||||
}
|
||||
|
||||
// 添加或编辑文集分类服务接口
|
||||
export const ServeEditArticleClass = data => {
|
||||
return post('/api/v1/article/edit-article-class', data)
|
||||
}
|
||||
|
||||
// 添加或编辑笔记标签服务接口
|
||||
export const ServeEditArticleTag = data => {
|
||||
return post('/api/v1/article/edit-article-tag', data)
|
||||
}
|
||||
|
||||
// 删除笔记分类服务接口
|
||||
export const ServeDeleteArticleClass = data => {
|
||||
return post('/api/v1/article/del-article-class', data)
|
||||
}
|
||||
|
||||
// 删除笔记标签服务接口
|
||||
export const ServeDeleteArticleTag = data => {
|
||||
return post('/api/v1/article/del-article-tag', data)
|
||||
}
|
||||
|
||||
// 笔记分类排序服务接口
|
||||
export const ServeArticleClassSort = data => {
|
||||
return post('/api/v1/article/article-class-sort', data)
|
||||
}
|
||||
|
||||
// 合并笔记分类服务接口
|
||||
export const ServeMergeArticleClass = data => {
|
||||
return post('/api/v1/article/merge-article-class', data)
|
||||
}
|
||||
|
||||
// 移动笔记服务接口
|
||||
export const ServeMoveArticle = data => {
|
||||
return post('/api/v1/article/move-article', data)
|
||||
}
|
||||
|
||||
// 设置标记星号笔记服务接口
|
||||
export const ServeSetAsteriskArticle = data => {
|
||||
return post('/api/v1/article/set-asterisk-article', data)
|
||||
}
|
||||
|
||||
// 编辑笔记服务接口
|
||||
export const ServeEditArticle = data => {
|
||||
return post('/api/v1/article/edit-article', data)
|
||||
}
|
||||
|
||||
// 删除笔记服务接口
|
||||
export const ServeDeleteArticle = data => {
|
||||
return post('/api/v1/article/delete-article', data)
|
||||
}
|
||||
|
||||
// 恢复笔记服务接口
|
||||
export const ServeRecoverArticle = data => {
|
||||
return post('/api/v1/article/recover-article', data)
|
||||
}
|
||||
|
||||
// 笔记图片上传服务接口
|
||||
export const ServeUploadArticleImg = data => {
|
||||
return upload('/api/v1/article/upload-article-image', data)
|
||||
}
|
||||
|
||||
// 笔记附件上传服务接口
|
||||
export const ServeUploadArticleAnnex = data => {
|
||||
return upload('/api/v1/article/upload-article-annex', data)
|
||||
}
|
||||
|
||||
// 移除笔记附件服务接口
|
||||
export const ServeDeleteArticleAnnex = data => {
|
||||
return post('/api/v1/article/delete-article-annex', data)
|
||||
}
|
||||
|
||||
// 恢复笔记附件服务接口
|
||||
export const ServeRecoverArticleAnnex = data => {
|
||||
return post('/api/v1/article/recover-article-annex', data)
|
||||
}
|
||||
|
||||
// 永久删除笔记附件回收站文件
|
||||
export const ServeForeverDeleteAnnex = data => {
|
||||
return post('/api/v1/article/forever-delete-annex', data)
|
||||
}
|
||||
|
||||
// 永久删除笔记回收站的笔记
|
||||
export const ServeForeverDeleteArticle = data => {
|
||||
return post('/api/v1/article/forever-delete-article', data)
|
||||
}
|
||||
|
||||
// 笔记附件回收站列表服务接口
|
||||
export const ServeGetRecoverAnnexList = () => {
|
||||
return get('/api/v1/article/recover-annex-list')
|
||||
}
|
||||
|
||||
// 下载笔记附件服务接口
|
||||
export const ServeDownloadAnnex = annex_id => {
|
||||
let api = config.BASE_API_URL
|
||||
try {
|
||||
let link = document.createElement('a')
|
||||
link.href = `${api}/api/v1/download/article-annex?annex_id=${annex_id}&token=${getToken()}`
|
||||
link.click()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新笔记标签服务接口
|
||||
export const ServeUpdateArticleTag = data => {
|
||||
return post('/api/v1/article/update-article-tag', data)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { post } from '@/utils/request'
|
||||
|
||||
export const DeviceIndex = data => {
|
||||
return post('/backend/device/index', data)
|
||||
}
|
||||
|
||||
export const DeviceAssoc = () => {
|
||||
return post('/backend/device/assoc', {})
|
||||
}
|
||||
|
||||
export const XianyuIndex = data => {
|
||||
return post('/backend/xianyu/index', data)
|
||||
}
|
||||
|
||||
export const XianyuRemark = data => {
|
||||
return post('/backend/xianyu/remark', data)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { post, get, upload } from '@/utils/request'
|
||||
|
||||
// 查询用户表情包服务接口
|
||||
export const ServeFindUserEmoticon = () => {
|
||||
return get('/api/v1/emoticon/user-emoticon')
|
||||
}
|
||||
|
||||
// 查询系统表情包服务接口
|
||||
export const ServeFindSysEmoticon = () => {
|
||||
return get('/api/v1/emoticon/system-emoticon')
|
||||
}
|
||||
|
||||
// 设置用户表情包服务接口
|
||||
export const ServeSetUserEmoticon = data => {
|
||||
return post('/api/v1/emoticon/set-user-emoticon', data)
|
||||
}
|
||||
|
||||
// 收藏表情包服务接口
|
||||
export const ServeCollectEmoticon = data => {
|
||||
return post('/api/v1/emoticon/collect-emoticon', data)
|
||||
}
|
||||
|
||||
// 移除收藏表情包服务接口
|
||||
export const ServeDelCollectEmoticon = data => {
|
||||
return post('/api/v1/emoticon/del-collect-emoticon', data)
|
||||
}
|
||||
|
||||
// 上传表情包服务接口
|
||||
export const ServeUploadEmoticon = data => {
|
||||
return upload('/api/v1/emoticon/upload-emoticon', data)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import { post } from '@/utils/request'
|
||||
|
||||
export const ProductIndex = data => {
|
||||
return post('/backend/product/index', data)
|
||||
}
|
||||
|
||||
export const ProductPrice = data => {
|
||||
return post('/backend/product/price', data)
|
||||
}
|
||||
|
||||
export const ProductStock = data => {
|
||||
return post('/backend/product/stock', data)
|
||||
}
|
||||
|
||||
export const ProductTitle = data => {
|
||||
return post('/backend/product/title', data)
|
||||
}
|
||||
|
||||
export const ProductContent = data => {
|
||||
return post('/backend/product/content', data)
|
||||
}
|
||||
|
||||
export const ProductLabel = data => {
|
||||
return post('/backend/product/label', data)
|
||||
}
|
||||
|
||||
export const ProductTheme = data => {
|
||||
return post('/backend/product/theme', data)
|
||||
}
|
||||
|
||||
export const ProductSave = data => {
|
||||
return post('/backend/product/save', data)
|
||||
}
|
||||
|
||||
export const ProductDelete = id => {
|
||||
return post('/backend/product/delete', { id })
|
||||
}
|
||||
|
||||
export const ProductGroupIndex = data => {
|
||||
return post('/backend/product_group/index', data)
|
||||
}
|
||||
|
||||
export const ProductGroupSave = data => {
|
||||
return post('/backend/product_group/save', data)
|
||||
}
|
||||
|
||||
export const ProductGroupDelete = id => {
|
||||
return post('/backend/product_group/delete', { id })
|
||||
}
|
||||
|
||||
export const ProductReleaseSave = data => {
|
||||
return post('/backend/product_release/save', data )
|
||||
}
|
||||
|
||||
export const ProductContentPoolAssoc = () => {
|
||||
return post('/backend/product_content_pool/assoc', { } )
|
||||
}
|
||||
|
||||
export const ProductContentPoolSave = data => {
|
||||
return post('/backend/product_content_pool/save', data )
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { post } from '@/utils/request'
|
||||
|
||||
export const RoleIndex = data => {
|
||||
return post('/manage/role/index', data)
|
||||
}
|
||||
|
||||
export const RoleSave = data => {
|
||||
return post('/manage/role/save', data)
|
||||
}
|
||||
|
||||
export const RolePrivileges = data => {
|
||||
return post('/manage/role/privileges', data)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { post } from '@/utils/request'
|
||||
|
||||
export const ServerIndex = data => {
|
||||
return post('/manage/server/index', data)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { post, upload } from '@/utils/request'
|
||||
|
||||
export const StatisticsIndex = data => {
|
||||
return post('/backend/statistics/index', data)
|
||||
}
|
||||
|
||||
export const StatisticsSummaryUserSmall = data => {
|
||||
return post('/backend/statistics_summary/userSmall', data)
|
||||
}
|
||||
|
||||
export const StatisticsSummaryPayMoney = data => {
|
||||
return post('/backend/statistics_summary/payMoney', data)
|
||||
}
|
||||
|
||||
export const StatisticsSummarySendNum = data => {
|
||||
return post('/backend/statistics_summary/sendNum', data)
|
||||
}
|
||||
|
||||
// 导出数据
|
||||
export const derive = data => {
|
||||
return post('/manage/statistics/derive', data)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { post } from '@/utils/request'
|
||||
|
||||
export const TaskIndex = data => {
|
||||
return post('/backend/task/index', data)
|
||||
}
|
||||
|
||||
export const TaskLog = data => {
|
||||
return post('/backend/task/log', data)
|
||||
}
|
||||
|
||||
export const TaskSave = data => {
|
||||
return post('/backend/task/save', data)
|
||||
}
|
||||
|
||||
export const TaskDelete = id => {
|
||||
return post('/backend/task/delete', { id })
|
||||
}
|
||||
|
||||
export const TaskBatchDelete = ids => {
|
||||
return post('/backend/task/batchDelete', { ids })
|
||||
}
|
||||
|
||||
export const TaskRunTypeAssoc = () => {
|
||||
return post('/backend/task/runTypeAssoc', { })
|
||||
}
|
||||
|
||||
export const TaskMessageReplyClose = data => {
|
||||
return post('/backend/message_reply/close', data)
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { post, get } from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param {Object} data 登录数据
|
||||
* @param {string} data.username 用户名
|
||||
* @param {string} data.password 密码
|
||||
* @param {boolean} data.is_encrypted 密码是否已加密
|
||||
* @returns {Promise} 登录结果
|
||||
*/
|
||||
export function ServeLogin(data) {
|
||||
return post('/api/auth/login', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号验证码登录
|
||||
* @param {Object} data 登录数据
|
||||
* @param {string} data.mobile 手机号
|
||||
* @param {string} data.code 验证码
|
||||
* @param {boolean} data.is_encrypted 验证码是否已加密
|
||||
* @returns {Promise} 登录结果
|
||||
*/
|
||||
export function ServeMobileLogin(data) {
|
||||
return post('/api/auth/mobile-login', data)
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
export const ServeSendCode = data => {
|
||||
return post('/api/auth/code', data)
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
export const ServeGetUser = () => {
|
||||
return get('/api/auth/info')
|
||||
}
|
||||
|
||||
// 刷新token
|
||||
export const ServeRefreshToken = () => {
|
||||
return post('/api/auth/refresh')
|
||||
}
|
||||
|
||||
export const ServeSetUserPassword = (data) => {
|
||||
return post('/backend/user/password', data)
|
||||
}
|
||||
|
||||
// 退出登录服务接口
|
||||
export const ServeLogout = () => {
|
||||
// JWT不需要服务端登出,直接清除本地token即可
|
||||
return Promise.resolve({ code: 200, msg: '退出成功' })
|
||||
}
|
||||
|
||||
export const UserIndex = data => {
|
||||
return post('/backend/user/index', data)
|
||||
}
|
||||
|
||||
export const UserGetUsername = data => {
|
||||
return post('/backend/user/username', data)
|
||||
}
|
||||
|
||||
export const UserSave = data => {
|
||||
return post('/backend/user/save', data)
|
||||
}
|
||||
|
||||
export const UserPassword = data => {
|
||||
return post('/backend/user/password', data)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
|
||||
.tk_header{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
@import "./reset.css";
|
||||
@import "./common.less";
|
||||
.no-select {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.no-padding {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.no-border {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.border-radius0 {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.full-height {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ov-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 滚动条样式
|
||||
.lum-scrollbar {
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
background-color: #e4e4e5;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 3px;
|
||||
background-color: #c0bebc;
|
||||
}
|
||||
}
|
||||
|
||||
.larkc-tag {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 6px;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
background-color: #dee0e3;
|
||||
transform: scale(0.83);
|
||||
transform-origin: left;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// 自定义 dialog 样式
|
||||
// lum-dialog -- start
|
||||
.lum-dialog-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: @maskBagColor;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.lum-dialog-box {
|
||||
min-width: 200px;
|
||||
min-height: 200px;
|
||||
background-color: white;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px 0 rgba(31, 35, 41, 0.2);
|
||||
margin: 0 10px;
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #f5eeee;
|
||||
|
||||
> p:first-child {
|
||||
text-indent: 20px;
|
||||
}
|
||||
|
||||
.tools {
|
||||
height: 100%;
|
||||
width: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 20px;
|
||||
|
||||
i {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
/deep/.el-input__inner {
|
||||
border-radius: 1px !important;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
border-radius: 2px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lum-dialog -- end
|
||||
|
||||
.talk-notify {
|
||||
.el-notification__title {
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
color: #f44336;
|
||||
}
|
||||
|
||||
p {
|
||||
max-height: 65px;
|
||||
overflow: hidden;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
text-indent: -7px;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
@@ -1,991 +0,0 @@
|
||||
.markdown-body .octicon {
|
||||
display: inline-block;
|
||||
fill: currentColor;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.markdown-body .anchor {
|
||||
float: left;
|
||||
line-height: 1;
|
||||
margin-left: -20px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.markdown-body .anchor:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.markdown-body h1 .octicon-link,
|
||||
.markdown-body h2 .octicon-link,
|
||||
.markdown-body h3 .octicon-link,
|
||||
.markdown-body h4 .octicon-link,
|
||||
.markdown-body h5 .octicon-link,
|
||||
.markdown-body h6 .octicon-link {
|
||||
color: #1b1f23;
|
||||
vertical-align: middle;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.markdown-body h1:hover .anchor,
|
||||
.markdown-body h2:hover .anchor,
|
||||
.markdown-body h3:hover .anchor,
|
||||
.markdown-body h4:hover .anchor,
|
||||
.markdown-body h5:hover .anchor,
|
||||
.markdown-body h6:hover .anchor {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-body h1:hover .anchor .octicon-link,
|
||||
.markdown-body h2:hover .anchor .octicon-link,
|
||||
.markdown-body h3:hover .anchor .octicon-link,
|
||||
.markdown-body h4:hover .anchor .octicon-link,
|
||||
.markdown-body h5:hover .anchor .octicon-link,
|
||||
.markdown-body h6:hover .anchor .octicon-link {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.markdown-body h1:hover .anchor .octicon-link:before,
|
||||
.markdown-body h2:hover .anchor .octicon-link:before,
|
||||
.markdown-body h3:hover .anchor .octicon-link:before,
|
||||
.markdown-body h4:hover .anchor .octicon-link:before,
|
||||
.markdown-body h5:hover .anchor .octicon-link:before,
|
||||
.markdown-body h6:hover .anchor .octicon-link:before {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
content: ' ';
|
||||
display: inline-block;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' width='16' height='16' aria-hidden='true'%3E%3Cpath fill-rule='evenodd' d='M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z'%3E%3C/path%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
font-size: 16px;
|
||||
word-wrap: break-word;
|
||||
font-family: Content-font, Roboto, sans-serif;
|
||||
font-weight: 400;
|
||||
color: #3B454E;
|
||||
line-height: 1.625;
|
||||
}
|
||||
|
||||
.markdown-body details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.markdown-body summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
.markdown-body a {
|
||||
background-color: initial;
|
||||
}
|
||||
|
||||
.markdown-body a:active,
|
||||
.markdown-body a:hover {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.markdown-body strong {
|
||||
font-weight: inherit;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.markdown-body h1 {
|
||||
font-size: 2em;
|
||||
margin: .67em 0;
|
||||
}
|
||||
|
||||
.markdown-body img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
.markdown-body code,
|
||||
.markdown-body kbd,
|
||||
.markdown-body pre {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.markdown-body hr {
|
||||
box-sizing: initial;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.markdown-body input {
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown-body input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.markdown-body [type=checkbox] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-body * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.markdown-body input {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.markdown-body a {
|
||||
color: #0366d6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-body a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.markdown-body strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body hr {
|
||||
height: 0;
|
||||
margin: 15px 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #dfe2e5;
|
||||
}
|
||||
|
||||
.markdown-body hr:after,
|
||||
.markdown-body hr:before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.markdown-body hr:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown-body table {
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.markdown-body td,
|
||||
.markdown-body th {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-body details summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.markdown-body kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
line-height: 10px;
|
||||
color: #444d56;
|
||||
vertical-align: middle;
|
||||
background-color: #fafbfc;
|
||||
border: 1px solid #d1d5da;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #d1d5da;
|
||||
}
|
||||
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body h1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.markdown-body h1,
|
||||
.markdown-body h2 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.markdown-body h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.markdown-body h3,
|
||||
.markdown-body h4 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown-body h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown-body p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.markdown-body blockquote {
|
||||
margin: 20px 0 !important;
|
||||
background-color: #f5f8fc;
|
||||
padding: 1rem;
|
||||
color: #8796a8;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.markdown-body ol,
|
||||
.markdown-body ul {
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body ol ol,
|
||||
.markdown-body ul ol {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
|
||||
.markdown-body ol ol ol,
|
||||
.markdown-body ol ul ol,
|
||||
.markdown-body ul ol ol,
|
||||
.markdown-body ul ul ol {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
|
||||
.markdown-body dd {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.markdown-body code,
|
||||
.markdown-body pre {
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown-body pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body input::-webkit-inner-spin-button,
|
||||
.markdown-body input::-webkit-outer-spin-button {
|
||||
margin: 0;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.markdown-body :checked+.radio-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border-color: #0366d6;
|
||||
}
|
||||
|
||||
.markdown-body .border {
|
||||
border: 1px solid #e1e4e8 !important;
|
||||
}
|
||||
|
||||
.markdown-body .border-0 {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body .border-bottom {
|
||||
border-bottom: 1px solid #e1e4e8 !important;
|
||||
}
|
||||
|
||||
.markdown-body .rounded-1 {
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
.markdown-body .bg-white {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.markdown-body .bg-gray-light {
|
||||
background-color: #fafbfc !important;
|
||||
}
|
||||
|
||||
.markdown-body .text-gray-light {
|
||||
color: #6a737d !important;
|
||||
}
|
||||
|
||||
.markdown-body .mb-0 {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body .my-2 {
|
||||
margin-top: 8px !important;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-0 {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body .py-0 {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-1 {
|
||||
padding-left: 4px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-2 {
|
||||
padding-left: 8px !important;
|
||||
}
|
||||
|
||||
.markdown-body .py-2 {
|
||||
padding-top: 8px !important;
|
||||
padding-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-3,
|
||||
.markdown-body .px-3 {
|
||||
padding-left: 16px !important;
|
||||
}
|
||||
|
||||
.markdown-body .px-3 {
|
||||
padding-right: 16px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-4 {
|
||||
padding-left: 24px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-5 {
|
||||
padding-left: 32px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-6 {
|
||||
padding-left: 40px !important;
|
||||
}
|
||||
|
||||
.markdown-body .f6 {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.markdown-body .lh-condensed {
|
||||
line-height: 1.25 !important;
|
||||
}
|
||||
|
||||
.markdown-body .text-bold {
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-c {
|
||||
color: #6a737d;
|
||||
}
|
||||
|
||||
.markdown-body .pl-c1,
|
||||
.markdown-body .pl-s .pl-v {
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.markdown-body .pl-e,
|
||||
.markdown-body .pl-en {
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.markdown-body .pl-s .pl-s1,
|
||||
.markdown-body .pl-smi {
|
||||
color: #24292e;
|
||||
}
|
||||
|
||||
.markdown-body .pl-ent {
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
.markdown-body .pl-k {
|
||||
color: #d73a49;
|
||||
}
|
||||
|
||||
.markdown-body .pl-pds,
|
||||
.markdown-body .pl-s,
|
||||
.markdown-body .pl-s .pl-pse .pl-s1,
|
||||
.markdown-body .pl-sr,
|
||||
.markdown-body .pl-sr .pl-cce,
|
||||
.markdown-body .pl-sr .pl-sra,
|
||||
.markdown-body .pl-sr .pl-sre {
|
||||
color: #032f62;
|
||||
}
|
||||
|
||||
.markdown-body .pl-smw,
|
||||
.markdown-body .pl-v {
|
||||
color: #e36209;
|
||||
}
|
||||
|
||||
.markdown-body .pl-bu {
|
||||
color: #b31d28;
|
||||
}
|
||||
|
||||
.markdown-body .pl-ii {
|
||||
color: #fafbfc;
|
||||
background-color: #b31d28;
|
||||
}
|
||||
|
||||
.markdown-body .pl-c2 {
|
||||
color: #fafbfc;
|
||||
background-color: #d73a49;
|
||||
}
|
||||
|
||||
.markdown-body .pl-c2:before {
|
||||
content: "^M";
|
||||
}
|
||||
|
||||
.markdown-body .pl-sr .pl-cce {
|
||||
font-weight: 700;
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
.markdown-body .pl-ml {
|
||||
color: #735c0f;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mh,
|
||||
.markdown-body .pl-mh .pl-en,
|
||||
.markdown-body .pl-ms {
|
||||
font-weight: 700;
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mi {
|
||||
font-style: italic;
|
||||
color: #24292e;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mb {
|
||||
font-weight: 700;
|
||||
color: #24292e;
|
||||
}
|
||||
|
||||
.markdown-body .pl-md {
|
||||
color: #b31d28;
|
||||
background-color: #ffeef0;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mi1 {
|
||||
color: #22863a;
|
||||
background-color: #f0fff4;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mc {
|
||||
color: #e36209;
|
||||
background-color: #ffebda;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mi2 {
|
||||
color: #f6f8fa;
|
||||
background-color: #005cc5;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mdr {
|
||||
font-weight: 700;
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.markdown-body .pl-ba {
|
||||
color: #586069;
|
||||
}
|
||||
|
||||
.markdown-body .pl-sg {
|
||||
color: #959da5;
|
||||
}
|
||||
|
||||
.markdown-body .pl-corl {
|
||||
text-decoration: underline;
|
||||
color: #032f62;
|
||||
}
|
||||
|
||||
.markdown-body .mb-0 {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body .my-2 {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.markdown-body .my-2 {
|
||||
margin-top: 8px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-0 {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body .py-0 {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-1 {
|
||||
padding-left: 4px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-2 {
|
||||
padding-left: 8px !important;
|
||||
}
|
||||
|
||||
.markdown-body .py-2 {
|
||||
padding-top: 8px !important;
|
||||
padding-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-3 {
|
||||
padding-left: 16px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-4 {
|
||||
padding-left: 24px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-5 {
|
||||
padding-left: 32px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-6 {
|
||||
padding-left: 40px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-7 {
|
||||
padding-left: 48px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-8 {
|
||||
padding-left: 64px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-9 {
|
||||
padding-left: 80px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-10 {
|
||||
padding-left: 96px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-11 {
|
||||
padding-left: 112px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-12 {
|
||||
padding-left: 128px !important;
|
||||
}
|
||||
|
||||
.markdown-body hr {
|
||||
border-bottom-color: #eee;
|
||||
}
|
||||
|
||||
.markdown-body kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
line-height: 10px;
|
||||
color: #444d56;
|
||||
vertical-align: middle;
|
||||
background-color: #fafbfc;
|
||||
border: 1px solid #d1d5da;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #d1d5da;
|
||||
}
|
||||
|
||||
.markdown-body:after,
|
||||
.markdown-body:before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.markdown-body:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown-body>:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body>:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body a:not([href]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-body blockquote,
|
||||
.markdown-body details,
|
||||
.markdown-body dl,
|
||||
.markdown-body ol,
|
||||
.markdown-body p,
|
||||
.markdown-body pre,
|
||||
.markdown-body table,
|
||||
.markdown-body ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markdown-body hr {
|
||||
height: .25em;
|
||||
padding: 0;
|
||||
margin: 24px 0;
|
||||
background-color: #e1e4e8;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.markdown-body blockquote {
|
||||
margin: 20px 0 !important;
|
||||
background-color: #f5f8fc;
|
||||
padding: 1rem;
|
||||
color: #8796a8;
|
||||
border-left: 3px solid #03A9F4;
|
||||
}
|
||||
|
||||
.markdown-body blockquote>:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.markdown-body blockquote>:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.markdown-body h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.markdown-body h1,
|
||||
.markdown-body h2 {
|
||||
padding-bottom: .3em;
|
||||
/* border-bottom: 1px solid #eaecef; */
|
||||
}
|
||||
|
||||
.markdown-body h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.markdown-body h3 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.markdown-body h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.markdown-body h5 {
|
||||
font-size: .875em;
|
||||
}
|
||||
|
||||
.markdown-body h6 {
|
||||
font-size: .85em;
|
||||
color: #6a737d;
|
||||
}
|
||||
|
||||
.markdown-body ol,
|
||||
.markdown-body ul {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.markdown-body ol ol,
|
||||
.markdown-body ol ul,
|
||||
.markdown-body ul ol,
|
||||
.markdown-body ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body li {
|
||||
word-wrap: break-all;
|
||||
}
|
||||
|
||||
.markdown-body li>p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.markdown-body li+li {
|
||||
margin-top: .25em;
|
||||
}
|
||||
|
||||
.markdown-body dl {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-body dl dt {
|
||||
padding: 0;
|
||||
margin-top: 16px;
|
||||
font-size: 1em;
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body dl dd {
|
||||
padding: 0 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markdown-body table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.markdown-body table th {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body table td,
|
||||
.markdown-body table th {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid #dfe2e5;
|
||||
}
|
||||
|
||||
.markdown-body table tr {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #c6cbd1;
|
||||
}
|
||||
|
||||
.markdown-body table tr:nth-child(2n) {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
|
||||
.markdown-body img {
|
||||
max-width: 100%;
|
||||
box-sizing: initial;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.markdown-body img[align=right] {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.markdown-body img[align=left] {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.markdown-body code {
|
||||
padding: .2em .4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27, 31, 35, .05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown-body pre {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
.markdown-body pre>code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 100%;
|
||||
word-break: normal;
|
||||
white-space: pre;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.markdown-body .highlight {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markdown-body .highlight pre {
|
||||
margin-bottom: 0;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.markdown-body .highlight pre,
|
||||
.markdown-body pre {
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.markdown-body pre code {
|
||||
display: inline;
|
||||
max-width: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
line-height: inherit;
|
||||
word-wrap: normal;
|
||||
background-color: initial;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.markdown-body .commit-tease-sha {
|
||||
display: inline-block;
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
font-size: 90%;
|
||||
color: #444d56;
|
||||
}
|
||||
|
||||
.markdown-body .full-commit .btn-outline:not(:disabled):hover {
|
||||
color: #005cc5;
|
||||
border-color: #005cc5;
|
||||
}
|
||||
|
||||
.markdown-body .blob-wrapper {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.markdown-body .blob-wrapper-embedded {
|
||||
max-height: 240px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.markdown-body .blob-num {
|
||||
width: 1%;
|
||||
min-width: 50px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: rgba(27, 31, 35, .3);
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
vertical-align: top;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.markdown-body .blob-num:hover {
|
||||
color: rgba(27, 31, 35, .6);
|
||||
}
|
||||
|
||||
.markdown-body .blob-num:before {
|
||||
content: attr(data-line-number);
|
||||
}
|
||||
|
||||
.markdown-body .blob-code {
|
||||
position: relative;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
line-height: 20px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.markdown-body .blob-code-inner {
|
||||
overflow: visible;
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
font-size: 12px;
|
||||
color: #24292e;
|
||||
word-wrap: normal;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.markdown-body .pl-token.active,
|
||||
.markdown-body .pl-token:hover {
|
||||
cursor: pointer;
|
||||
background: #ffea7f;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="1"] {
|
||||
-moz-tab-size: 1;
|
||||
tab-size: 1;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="2"] {
|
||||
-moz-tab-size: 2;
|
||||
tab-size: 2;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="3"] {
|
||||
-moz-tab-size: 3;
|
||||
tab-size: 3;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="4"] {
|
||||
-moz-tab-size: 4;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="5"] {
|
||||
-moz-tab-size: 5;
|
||||
tab-size: 5;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="6"] {
|
||||
-moz-tab-size: 6;
|
||||
tab-size: 6;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="7"] {
|
||||
-moz-tab-size: 7;
|
||||
tab-size: 7;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="8"] {
|
||||
-moz-tab-size: 8;
|
||||
tab-size: 8;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="9"] {
|
||||
-moz-tab-size: 9;
|
||||
tab-size: 9;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="10"] {
|
||||
-moz-tab-size: 10;
|
||||
tab-size: 10;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="11"] {
|
||||
-moz-tab-size: 11;
|
||||
tab-size: 11;
|
||||
}
|
||||
|
||||
.markdown-body .tab-size[data-tab-size="12"] {
|
||||
-moz-tab-size: 12;
|
||||
tab-size: 12;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item+.task-list-item {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item input {
|
||||
margin: 0 .2em .25em -1.6em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
.aside-box {
|
||||
position: relative;
|
||||
background-color: white;
|
||||
border-right: 1px solid rgb(245, 245, 245);
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
|
||||
.from {
|
||||
flex: 1 1;
|
||||
flex-shrink: 0;
|
||||
height: 40px;
|
||||
|
||||
/deep/.el-input .el-input__inner {
|
||||
border-radius: 20px;
|
||||
width: 170px;
|
||||
}
|
||||
}
|
||||
|
||||
.tools {
|
||||
flex-basis: 32px;
|
||||
flex-shrink: 0;
|
||||
height: 32px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
|
||||
.tools-menu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 38px;
|
||||
width: 100px;
|
||||
min-height: 80px;
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(31, 35, 41, 0.9);
|
||||
border-radius: 5px;
|
||||
z-index: 1;
|
||||
padding: 3px 0;
|
||||
|
||||
.menu1-item {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(70, 72, 73, 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 右侧面板
|
||||
.panel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
&.border{
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.subheader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
border-top: 1px solid rgb(92, 156, 230);
|
||||
border-bottom: 1px solid rgb(92, 156, 230);
|
||||
|
||||
p {
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #508afe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.preloading {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
user-select: none;
|
||||
|
||||
p {
|
||||
margin-top: 20px;
|
||||
color: #afacac;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
||||
.data-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
cursor: pointer;
|
||||
padding: 5px 15px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #f1ebeb;
|
||||
margin-bottom: 2px;
|
||||
|
||||
.avatar {
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
flex-basis: 35px;
|
||||
flex-shrink: 0;
|
||||
background-color: #508afe;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: white;
|
||||
user-select: none;
|
||||
transition: ease 1s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
flex-direction: column;
|
||||
flex: 1 1;
|
||||
margin-left: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.name {
|
||||
margin-right: 15px;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.larkc-tag {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 6px;
|
||||
height: 20px;
|
||||
border-radius: 2px;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
background-color: #dee0e3;
|
||||
transform: scale(0.8);
|
||||
transform-origin: left;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.wait {
|
||||
background: #ffb445;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.agree {
|
||||
background: #53bd53;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 10px;
|
||||
line-height: 18px;
|
||||
color: #8f959e;
|
||||
overflow: hidden;
|
||||
margin-top: 3px;
|
||||
font-weight: 300;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.apply-from {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
right: -110px;
|
||||
top: 0px;
|
||||
height: 60px;
|
||||
width: 100px;
|
||||
transition: ease 0.5s 0.3s;
|
||||
background-color: white;
|
||||
opacity: 0;
|
||||
button {
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 8px 4px #f1f1f1;
|
||||
|
||||
.avatar {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.apply-from {
|
||||
opacity: 1;
|
||||
right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.broadside-box {
|
||||
position: absolute;
|
||||
width: 350px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
animation: showBox 0.5s ease-in-out;
|
||||
-webkit-animation: showBox 0.5s ease-in-out;
|
||||
-moz-animation: showBox 0.5s ease-in-out;
|
||||
-webkit-box-direction: normal;
|
||||
background: white;
|
||||
box-shadow: 0 0 14px #cccccc70;
|
||||
}
|
||||
|
||||
@keyframes showBox {
|
||||
0% {
|
||||
transform: translateX(350px);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes showBox {
|
||||
0% {
|
||||
-webkit-transform: translateX(350px);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translateX(0);
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
/deep/.el-input__inner {
|
||||
border-radius: 1px !important;
|
||||
}
|
||||
|
||||
#auth-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: #f6f8fb;
|
||||
|
||||
#logo-name {
|
||||
width: 200px;
|
||||
height: 38px;
|
||||
font-size: 34px;
|
||||
font-family: Times New Roman, Georgia, Serif;
|
||||
color: #2196f3;
|
||||
margin-left: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#login-box {
|
||||
position: absolute;
|
||||
width: 350px;
|
||||
min-height: 300px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 0 #ccc;
|
||||
box-shadow: 0 4px 14px 0 rgba(206, 207, 209, 0.5);
|
||||
padding: 10px 20px;
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
font-size: 22px;
|
||||
margin: 25px 0 20px 0;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
}
|
||||
|
||||
.send-code-btn {
|
||||
width: 140px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
display: inline-block;
|
||||
background: #f3ecec;
|
||||
text-align: center;
|
||||
color: #777373;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
margin-left: 5px;
|
||||
|
||||
&:active {
|
||||
background: #e4dbdb;
|
||||
}
|
||||
}
|
||||
|
||||
.send-sms-disable {
|
||||
cursor: not-allowed !important;
|
||||
background: #f7f7f7 !important;
|
||||
color: silver !important;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-account {
|
||||
text-align: center;
|
||||
|
||||
p {
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
color: rgb(45, 44, 44);
|
||||
font-weight: 100;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.copyright {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 70%;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
font-size: 12px;
|
||||
color: #b1a0a0;
|
||||
|
||||
a {
|
||||
color: #777272;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 500px) {
|
||||
.copyright {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fly-box {
|
||||
.fly {
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.bg-fly-circle1 {
|
||||
left: 40px;
|
||||
top: 100px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(100, 84, 239, 0.07) 0%,
|
||||
rgba(48, 33, 236, 0.04) 100%
|
||||
);
|
||||
animation: move 2.5s linear infinite;
|
||||
}
|
||||
|
||||
.bg-fly-circle2 {
|
||||
left: 3%;
|
||||
top: 60%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(100, 84, 239, 0.08) 0%,
|
||||
rgba(48, 33, 236, 0.04) 100%
|
||||
);
|
||||
animation: move 3s linear infinite;
|
||||
}
|
||||
|
||||
.bg-fly-circle3 {
|
||||
right: 2%;
|
||||
top: 140px;
|
||||
width: 145px;
|
||||
height: 145px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(100, 84, 239, 0.1) 0%,
|
||||
rgba(48, 33, 236, 0.04) 100%
|
||||
);
|
||||
animation: move 2.5s linear infinite;
|
||||
}
|
||||
|
||||
.bg-fly-circle4 {
|
||||
right: 5%;
|
||||
top: 60%;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(100, 84, 239, 0.02) 0%,
|
||||
rgba(48, 33, 236, 0.04) 100%
|
||||
);
|
||||
animation: move 3.5s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes move {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(25px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
.note-container {
|
||||
height: 100%;
|
||||
|
||||
/deep/.el-scrollbar__wrap {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.el-aside-one {
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
|
||||
.btn-header {
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
padding: 0;
|
||||
|
||||
/deep/.btn-dropdown-menu {
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/deep/button {
|
||||
height: 35px;
|
||||
line-height: 10px;
|
||||
font-weight: 400;
|
||||
|
||||
&:first-child {
|
||||
width: 160px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.note-headline {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
color: #353434;
|
||||
padding-left: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.note-aside {
|
||||
height: calc(100% - 100px) !important;
|
||||
overflow-y: auto;
|
||||
user-select: none;
|
||||
|
||||
.note-list-first {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding-left: 20px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background: #f6f3f3;
|
||||
}
|
||||
|
||||
> span {
|
||||
padding-left: 5px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-menu-nav {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.note-list-two {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding-left: 30px;
|
||||
cursor: pointer;
|
||||
|
||||
i {
|
||||
color: #448aff;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 176px;
|
||||
border: 1px solid #66b1ff;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
margin-left: 5px;
|
||||
padding: 2px 3px;
|
||||
font-size: 12px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #448aff;
|
||||
}
|
||||
}
|
||||
|
||||
.note-list-active {
|
||||
background: #f6f3f3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-aside-two {
|
||||
background-color: #f4f6f9;
|
||||
}
|
||||
|
||||
.el-aside-two .search-header {
|
||||
height: 60px;
|
||||
background: #f4f6f9;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 19px;
|
||||
font-size: 20px;
|
||||
color: #505050;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 30px;
|
||||
width: 80%;
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 15px;
|
||||
padding: 0 3px;
|
||||
font-size: 14px;
|
||||
background: #f4f6f9;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: rgb(158, 150, 150);
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-aside-two .title-header {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
position: relative;
|
||||
color: #7b7777;
|
||||
border-top: 1px solid #ece8e8;
|
||||
border-bottom: 1px solid #ece8e8;
|
||||
user-select: none;
|
||||
|
||||
span {
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.load-span {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
color: #6dabdc;
|
||||
font-size: 12px;
|
||||
|
||||
.sort-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
color: #7b7777;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-aside-two .article-main {
|
||||
width: 100%;
|
||||
height: calc(100% - 102px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
.note-empty {
|
||||
height: 150px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin-top: 30%;
|
||||
|
||||
.svg-notebook {
|
||||
width: 110px;
|
||||
height: 122px;
|
||||
font-size: 100px;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.article-row {
|
||||
min-height: 50px;
|
||||
border-bottom: 2px solid #ffffff;
|
||||
padding: 10px 10px 5px 10px;
|
||||
position: relative;
|
||||
|
||||
.article-title {
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
width: 327px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.article-tool {
|
||||
height: 20px;
|
||||
width: 56px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 10px;
|
||||
display: none;
|
||||
text-align: right;
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
margin-left: 2px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.el-icon-edit-outline {
|
||||
color: rgb(64, 158, 255);
|
||||
}
|
||||
|
||||
.el-icon-delete {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.recover-note {
|
||||
color: rgb(64, 158, 255);
|
||||
}
|
||||
}
|
||||
|
||||
.article-items {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.article-item {
|
||||
flex-grow: 1;
|
||||
|
||||
.article-date {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 4px;
|
||||
font-size: 10px;
|
||||
color: #a7afbc;
|
||||
font-weight: 300;
|
||||
line-height: 1.6;
|
||||
|
||||
span {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.article-abstract {
|
||||
display: -webkit-box;
|
||||
flex-grow: 1;
|
||||
max-height: 42px;
|
||||
font-size: 12px;
|
||||
color: #989494;
|
||||
line-height: 1.6;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.item-image {
|
||||
width: calc(100% - 56px - 16px);
|
||||
}
|
||||
}
|
||||
|
||||
.article-image {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
margin-left: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
/deep/.el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/deep/.el-image__error {
|
||||
font-size: 10px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.article-image /deep/.el-image__error {
|
||||
background: #f5f7fa;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active-row {
|
||||
background: white;
|
||||
|
||||
.article-title {
|
||||
width: 270px;
|
||||
}
|
||||
|
||||
.article-tool {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
min-width: 500px;
|
||||
font-family: "Microsoft YaHei";
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
a,
|
||||
img {
|
||||
-webkit-touch-callout: none
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: none;
|
||||
outline: 0;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
border: none;
|
||||
background: #fff;
|
||||
font-family: "Microsoft YaHei";
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: block;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
.message-group {
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
flex-direction: row;
|
||||
padding: 3px 12px 3px 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.left-box {
|
||||
width: 50px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
padding-top: 8px;
|
||||
|
||||
img {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.right-box {
|
||||
flex: auto;
|
||||
overflow-x: auto;
|
||||
padding: 0px 5px 15px 5px;
|
||||
|
||||
.msg-header {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
color: #a09a9a;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
|
||||
.name {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/.text-message {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
//主题皮肤 - 预留功能
|
||||
:root {
|
||||
--themeBagColor: red;
|
||||
}
|
||||
|
||||
// 默认主题
|
||||
.theme-default {
|
||||
--themeBagColor: #fff;
|
||||
}
|
||||
|
||||
// 深黑主题
|
||||
.theme-dark {
|
||||
--themeBagColor: black;
|
||||
}
|
||||
|
||||
// 红色主题
|
||||
.theme-red {
|
||||
--themeBagColor: red;
|
||||
}
|
||||
|
||||
// 浅蓝主题
|
||||
.theme-blue {
|
||||
--themeBagColor: blue;
|
||||
}
|
||||
|
||||
// ------- 定义 Less 变量 -------
|
||||
@themeBagColor: var(--themeBagColor);
|
||||
|
||||
// 遮罩层背景颜色
|
||||
@maskBagColor: rgba(31, 35, 41, .3);
|
||||
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 830 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 789 KiB |
|
Before Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 671 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 758 B |
|
Before Width: | Height: | Size: 882 B |
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,441 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-container class="editor-container">
|
||||
<el-header class="no-padding toolbar" height="35px">
|
||||
<ul>
|
||||
<li v-popover:popoverEmoticon>
|
||||
<i class="iconfont icon-icon_im_face" style="font-size: 15px" />
|
||||
<p class="tip-title">表情符号</p>
|
||||
</li>
|
||||
<li @click="$refs.restFile.click()">
|
||||
<i class="el-icon-picture-outline-round" />
|
||||
<p class="tip-title">图片</p>
|
||||
</li>
|
||||
<li @click="$refs.materialManage.show()" style="width: 56px; font-size: 13px; color: #409EFF;">
|
||||
素材库
|
||||
</li>
|
||||
<!--<li @click="codeBlock.isShow = true">
|
||||
<i class="iconfont icon-daima" />
|
||||
<p class="tip-title">代码片段</p>
|
||||
</li>
|
||||
<li @click="recorder = true">
|
||||
<i class="el-icon-headset" />
|
||||
<p class="tip-title">语音消息</p>
|
||||
</li>
|
||||
<li @click="$refs.restFile.click()">
|
||||
<i class="el-icon-picture-outline-round" />
|
||||
<p class="tip-title">图片</p>
|
||||
</li>
|
||||
<li @click="$refs.restFile2.click()">
|
||||
<i class="el-icon-folder" />
|
||||
<p class="tip-title">附件</p>
|
||||
</li>-->
|
||||
<!--<li @click="filesManager.isShow = true">
|
||||
<i class="el-icon-folder-opened" />
|
||||
<p class="tip-title">上传管理</p>
|
||||
</li>-->
|
||||
|
||||
<p class="text-tips no-select">
|
||||
<span>按Enter发送 / Shift+Enter 换行</span>
|
||||
<el-popover placement="top-end" width="600" trigger="click">
|
||||
<div class="editor-books">
|
||||
<div class="books-title">编辑说明:</div>
|
||||
<p>
|
||||
1.
|
||||
支持上传QQ及微信截图,在QQ或微信中截图后使用Ctrl+v上传图片。
|
||||
</p>
|
||||
<p>
|
||||
2.
|
||||
支持浏览器及Word文档中的图片复制上传、复制后使用Ctrl+v上传图片。
|
||||
</p>
|
||||
<p>3. 支持图片拖拽上传。</p>
|
||||
<!--<p>4. 支持视频上传 ( 文件小于30M ) 。</p>-->
|
||||
<p>4. 按Enter发送 / Shift+Enter 换行。</p>
|
||||
<p>5.注意:当图片和视频正在上传时,请勿关闭网页或离开当前对话框,否则将导致文件停止上传或上传失败。</p>
|
||||
</div>
|
||||
<i class="el-icon-info" slot="reference" />
|
||||
</el-popover>
|
||||
</p>
|
||||
</ul>
|
||||
|
||||
<el-popover
|
||||
ref="popoverEmoticon"
|
||||
placement="top-start"
|
||||
width="500"
|
||||
trigger="click"
|
||||
popper-class="no-padding"
|
||||
>
|
||||
<MeEditorEmoticon ref="editorEmoticon" @selected="selecteEmoticon" />
|
||||
</el-popover>
|
||||
|
||||
<form
|
||||
enctype="multipart/form-data"
|
||||
style="display: none"
|
||||
ref="fileFrom"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
ref="restFile"
|
||||
accept="image/*"
|
||||
@change="uploadImageChange"
|
||||
/>
|
||||
<input type="file" ref="restFile2" @change="uploadFileChange" />
|
||||
</form>
|
||||
</el-header>
|
||||
<el-main class="no-padding textarea">
|
||||
<textarea
|
||||
ref="textarea"
|
||||
v-paste="pasteImage"
|
||||
v-drag="dragPasteImage"
|
||||
v-model.trim="editorText"
|
||||
rows="6"
|
||||
placeholder="你想要聊点什么呢 ..."
|
||||
@keydown="keydownEvent($event)"
|
||||
/>
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
||||
<!-- 图片查看器 -->
|
||||
<MeEditorImageView
|
||||
ref="imageViewer"
|
||||
v-model="imageViewer.isShow"
|
||||
:file="imageViewer.file"
|
||||
@confirm="confirmUploadImage"
|
||||
/>
|
||||
|
||||
<MeEditorRecorder v-if="recorder" @close="recorder = false" />
|
||||
|
||||
<!-- 代码块编辑器 -->
|
||||
<TalkCodeBlock
|
||||
v-if="codeBlock.isShow"
|
||||
:edit-mode="codeBlock.editMode"
|
||||
@close="codeBlock.isShow = false"
|
||||
@confirm="confirmCodeBlock"
|
||||
/>
|
||||
|
||||
<!-- 文件上传管理器 -->
|
||||
<MeEditorFileManage ref="filesManager" v-model="filesManager.isShow" />
|
||||
|
||||
<material-manage ref="materialManage" @confirm="confirmUploadMaterials" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MeEditorEmoticon from './MeEditorEmoticon'
|
||||
import MeEditorFileManage from './MeEditorFileManage'
|
||||
import MeEditorImageView from './MeEditorImageView'
|
||||
import MeEditorRecorder from './MeEditorRecorder'
|
||||
import TalkCodeBlock from '@/components/chat/TalkCodeBlock'
|
||||
import { getPasteImgs, getDragPasteImg } from '@/utils/editor'
|
||||
import { findTalk } from '@/utils/talk'
|
||||
import MaterialManage from "@/views/message/components/MaterialManage";
|
||||
|
||||
export default {
|
||||
name: 'MeEditor',
|
||||
components: {
|
||||
MaterialManage,
|
||||
MeEditorEmoticon,
|
||||
MeEditorFileManage,
|
||||
MeEditorImageView,
|
||||
TalkCodeBlock,
|
||||
MeEditorRecorder,
|
||||
},
|
||||
computed: {
|
||||
talkUser() {
|
||||
return this.$store.state.dialogue.index_name
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
talkUser(n_index_name) {
|
||||
this.$refs.filesManager.clear()
|
||||
this.editorText = this.getDraftText(n_index_name)
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 当前编辑的内容
|
||||
editorText: '',
|
||||
|
||||
// 图片查看器相关信息
|
||||
imageViewer: {
|
||||
isShow: false,
|
||||
file: null,
|
||||
},
|
||||
|
||||
codeBlock: {
|
||||
isShow: false,
|
||||
editMode: true,
|
||||
},
|
||||
|
||||
filesManager: {
|
||||
isShow: false,
|
||||
},
|
||||
|
||||
// 录音器
|
||||
recorder: false,
|
||||
|
||||
// 上次发送消息的时间
|
||||
sendtime: 0,
|
||||
|
||||
// 发送间隔时间(默认1秒)
|
||||
interval: 1000,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setText(text) {
|
||||
this.editorText = text
|
||||
},
|
||||
|
||||
// 读取对话编辑草稿信息
|
||||
getDraftText(index_name) {
|
||||
return findTalk(index_name).draft_text || ''
|
||||
},
|
||||
|
||||
//复制粘贴图片回调方法
|
||||
pasteImage(e) {
|
||||
let files = getPasteImgs(e)
|
||||
if (files.length == 0) return
|
||||
|
||||
this.openImageViewer(files[0])
|
||||
},
|
||||
|
||||
//拖拽上传图片回调方法
|
||||
dragPasteImage(e) {
|
||||
let files = getDragPasteImg(e)
|
||||
if (files.length == 0) return
|
||||
|
||||
this.openImageViewer(files[0])
|
||||
},
|
||||
|
||||
// 键盘按下监听事件
|
||||
keydownEvent(e) {
|
||||
if (e.keyCode == 13 && this.editorText == '') {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
// 回车发送消息
|
||||
if (e.keyCode == 13 && e.shiftKey == false && this.editorText != '') {
|
||||
this.$emit('send', this.editorText)
|
||||
this.editorText = ''
|
||||
e.preventDefault()
|
||||
}
|
||||
},
|
||||
|
||||
// 选择图片文件后回调方法
|
||||
uploadImageChange(e) {
|
||||
this.openImageViewer(e.target.files[0])
|
||||
this.$refs.restFile.value = null
|
||||
},
|
||||
|
||||
// 选择文件回调事件
|
||||
uploadFileChange(e) {
|
||||
let maxsize = 30 * 1024 * 1024
|
||||
if (e.target.files.length == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
let file = e.target.files[0]
|
||||
if (/\.(gif|jpg|jpeg|png|webp|GIF|JPG|PNG|WEBP)$/.test(file.name)) {
|
||||
this.openImageViewer(file)
|
||||
return
|
||||
}
|
||||
|
||||
if (file.size > maxsize) {
|
||||
this.$notify.info({
|
||||
title: '消息',
|
||||
message: '上传文件不能大于30M',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.filesManager.isShow = true
|
||||
this.$refs.restFile2.value = null
|
||||
this.$refs.filesManager.upload(file)
|
||||
},
|
||||
|
||||
// 打开图片查看器
|
||||
openImageViewer(file) {
|
||||
this.imageViewer.isShow = true
|
||||
this.imageViewer.file = file
|
||||
},
|
||||
|
||||
// 确认上传图片消息回调事件
|
||||
confirmUploadImage() {
|
||||
let fileData = new FormData()
|
||||
fileData.append('image', this.imageViewer.file)
|
||||
|
||||
let ref1 = this.$refs.imageViewer
|
||||
ref1.loading = false
|
||||
ref1.closeBox()
|
||||
this.$emit('send-image', fileData)
|
||||
},
|
||||
|
||||
confirmUploadMaterials(materials) {
|
||||
this.$emit('send-materials', materials)
|
||||
},
|
||||
|
||||
// 选中表情包回调事件
|
||||
selecteEmoticon(data) {
|
||||
if (data.type == 1) {
|
||||
let value = this.editorText
|
||||
let el = this.$refs.textarea
|
||||
let startPos = el.selectionStart
|
||||
let endPos = el.selectionEnd
|
||||
let newValue =
|
||||
value.substring(0, startPos) +
|
||||
data.value +
|
||||
value.substring(endPos, value.length)
|
||||
|
||||
this.editorText = newValue
|
||||
|
||||
if (el.setSelectionRange) {
|
||||
setTimeout(() => {
|
||||
let index = startPos + data.value.length
|
||||
el.setSelectionRange(index, index)
|
||||
el.focus()
|
||||
}, 0)
|
||||
}
|
||||
} else {
|
||||
const { source, receive_id } = this.$store.state.dialogue
|
||||
ServeSendEmoticon({
|
||||
source,
|
||||
receive_id,
|
||||
emoticon_id: data.value,
|
||||
})
|
||||
}
|
||||
|
||||
this.$refs.popoverEmoticon.doClose()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.editor-container {
|
||||
height: 160px;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.editor-container .toolbar {
|
||||
line-height: 35px;
|
||||
border-bottom: 1px solid #f5f0f0;
|
||||
border-top: 1px solid #f5f0f0;
|
||||
}
|
||||
|
||||
.editor-container .toolbar li {
|
||||
list-style: none;
|
||||
float: left;
|
||||
width: 35px;
|
||||
margin-left: 3px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
line-height: 35px;
|
||||
position: relative;
|
||||
color: #8d8d8d;
|
||||
}
|
||||
|
||||
.editor-container .toolbar li .tip-title {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 38px;
|
||||
left: 0px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
background-color: rgba(31, 35, 41, 0.9);
|
||||
color: white;
|
||||
min-width: 30px;
|
||||
font-size: 10px;
|
||||
padding: 0 5px;
|
||||
border-radius: 2px;
|
||||
white-space: pre;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.editor-container .toolbar li:hover .tip-title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.editor-container .toolbar li:hover {
|
||||
background-color: #f7f5f5;
|
||||
}
|
||||
|
||||
.editor-container .toolbar .text-tips {
|
||||
float: right;
|
||||
margin-right: 15px;
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.editor-container .toolbar .text-tips i {
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
color: rgb(255, 181, 111);
|
||||
}
|
||||
|
||||
.editor-container .textarea {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: calc(100% - 10px);
|
||||
width: -moz-calc(100% - 10px);
|
||||
width: -webkit-calc(100% - 10px);
|
||||
height: calc(100% - 10px);
|
||||
height: -moz-calc(100% - 10px);
|
||||
height: -webkit-calc(100% - 10px);
|
||||
border: 0 none;
|
||||
outline: none;
|
||||
resize: none;
|
||||
font-size: 15px;
|
||||
overflow-y: auto;
|
||||
color: #464545;
|
||||
padding: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
textarea::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
textarea::-webkit-scrollbar-thumb {
|
||||
background: #d5cfcf;
|
||||
}
|
||||
|
||||
textarea::-webkit-scrollbar-track {
|
||||
background: #ededed;
|
||||
}
|
||||
|
||||
textarea::-webkit-input-placeholder {
|
||||
color: #dccdcd;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 编辑器文档说明 --- start */
|
||||
.editor-books .books-title {
|
||||
font-size: 16px;
|
||||
height: 30px;
|
||||
line-height: 22px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid #cbcbcb;
|
||||
color: #726f6f;
|
||||
font-weight: 400;
|
||||
margin-left: 11px;
|
||||
}
|
||||
|
||||
.editor-books p {
|
||||
text-indent: 10px;
|
||||
font-size: 12px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
color: #7f7c7c;
|
||||
}
|
||||
|
||||
/* 编辑器文档说明 --- end */
|
||||
</style>
|
||||
@@ -1,348 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-container class="container">
|
||||
<el-header height="30px" class="no-padding header">
|
||||
<span v-text="showTitle"></span>
|
||||
<!--<div class="addbtn" @click="systemEmojiBox = true">系统表情</div>-->
|
||||
</el-header>
|
||||
<el-main class="no-padding main lum-scrollbar">
|
||||
<input
|
||||
type="file"
|
||||
ref="fileCustomEmoji"
|
||||
accept="image/*"
|
||||
style="display: none"
|
||||
@change="customUploadEmoji"
|
||||
/>
|
||||
|
||||
<div v-show="showEmoticonId == -1" class="emoticon">
|
||||
<div class="title">QQ表情</div>
|
||||
<div
|
||||
v-for="(elImg, text) in emoji.emojis"
|
||||
v-html="elImg"
|
||||
class="emoticon-item"
|
||||
@click="clickEmoticon(text)"
|
||||
></div>
|
||||
<div class="clear"></div>
|
||||
<div class="title">符号表情</div>
|
||||
<div
|
||||
v-for="(item, i) in emoji.symbol"
|
||||
:key="i"
|
||||
class="emoticon-item symbol"
|
||||
@click="clickEmoticon(item)"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="item in emojiItem.slice(1)"
|
||||
v-show="item.emoticon_id == showEmoticonId"
|
||||
:key="item.emoticon_id"
|
||||
class="emoji-box"
|
||||
>
|
||||
<!--<div
|
||||
v-if="item.emoticon_id == 0"
|
||||
class="emoji-item custom-emoji"
|
||||
@click="$refs.fileCustomEmoji.click()"
|
||||
>
|
||||
<i class="el-icon-picture" />
|
||||
<span>自定义</span>
|
||||
</div>-->
|
||||
<div
|
||||
v-for="subitem in item.list"
|
||||
:key="subitem.src"
|
||||
class="emoji-item"
|
||||
@click="clickImageEmoticon(subitem)"
|
||||
>
|
||||
<el-image :src="subitem.src" fit="cover" />
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</el-main>
|
||||
<el-footer height="40px" class="no-padding footer">
|
||||
<div class="toolbar-items">
|
||||
<div
|
||||
v-show="emojiItem.length > 13"
|
||||
class="toolbar-item prev-page"
|
||||
@click="turnPage(1)"
|
||||
>
|
||||
<i class="el-icon-caret-left" />
|
||||
</div>
|
||||
<div
|
||||
v-for="(item, index) in showItems"
|
||||
:key="index"
|
||||
class="toolbar-item"
|
||||
@click="triggerItem(item)"
|
||||
>
|
||||
<img :src="item.url" />
|
||||
<p class="title">{{ item.name }}</p>
|
||||
</div>
|
||||
<div
|
||||
v-show="emojiItem.length > 13 && showItems.length == 13"
|
||||
class="toolbar-item next-page"
|
||||
@click="turnPage(2)"
|
||||
>
|
||||
<i class="el-icon-caret-right" />
|
||||
</div>
|
||||
</div>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
|
||||
<MeEditorSystemEmoticon
|
||||
v-if="systemEmojiBox"
|
||||
@close="systemEmojiBox = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MeEditorSystemEmoticon from '@/components/editor/MeEditorSystemEmoticon'
|
||||
import { emojiList as emoji } from '@/utils/emojis'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'MeEditorEmoticon',
|
||||
components: {
|
||||
MeEditorSystemEmoticon,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
emojiItem: state => state.emoticon.items,
|
||||
}),
|
||||
showItems() {
|
||||
let start = (this.page - 1) * this.pageSize
|
||||
let end = start + this.pageSize
|
||||
return this.emojiItem.slice(start, end - 1)
|
||||
},
|
||||
pageTotal() {
|
||||
return this.emojiItem.length / this.pageSize
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
emoji,
|
||||
|
||||
// 系统表情包套弹出窗
|
||||
systemEmojiBox: false,
|
||||
|
||||
showEmoticonId: -1,
|
||||
showTitle: 'QQ表情/符号表情',
|
||||
|
||||
page: 1,
|
||||
pageSize: 13,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
//this.$store.commit('LOAD_USER_EMOTICON')
|
||||
},
|
||||
methods: {
|
||||
// 表情包导航翻页
|
||||
turnPage(type) {
|
||||
if (type == 1) {
|
||||
if (this.page == 1) return false
|
||||
this.page--
|
||||
} else {
|
||||
if (this.page >= this.pageTotal) return false
|
||||
this.page++
|
||||
}
|
||||
},
|
||||
|
||||
// 点击表情包导航
|
||||
triggerItem(item) {
|
||||
this.showEmoticonId = item.emoticon_id
|
||||
this.showTitle = item.name
|
||||
},
|
||||
|
||||
// 选中表情
|
||||
clickEmoticon(emoji) {
|
||||
this.callback({
|
||||
type: 1,
|
||||
value: emoji,
|
||||
})
|
||||
},
|
||||
|
||||
// 发送图片表情包
|
||||
clickImageEmoticon(item) {
|
||||
this.callback({
|
||||
type: 2,
|
||||
value: item.media_id,
|
||||
})
|
||||
},
|
||||
|
||||
callback(data) {
|
||||
this.$emit('selected', data)
|
||||
},
|
||||
|
||||
// 自定义上传表情
|
||||
customUploadEmoji(e) {
|
||||
if (e.target.files.length == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.$store.commit('UPLOAD_USER_EMOTICON', {
|
||||
file: e.target.files[0],
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
height: 300px;
|
||||
width: 500px;
|
||||
background-color: white;
|
||||
|
||||
.header {
|
||||
line-height: 30px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
padding-left: 5px;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
border-bottom: 1px solid #fbf5f5;
|
||||
|
||||
.addbtn {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 1px;
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #eff1f7;
|
||||
|
||||
.toolbar-items {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.toolbar-item {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin: 0 2px;
|
||||
background-color: #fff;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
left: 0px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
background: #353434;
|
||||
color: white;
|
||||
min-width: 30px;
|
||||
font-size: 10px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
border-radius: 2px;
|
||||
white-space: pre;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:hover .title {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container .footer .toolbar-items .prev-page:active i,
|
||||
.container .footer .toolbar-items .next-page:active i {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.emoji-box,
|
||||
.emoticon {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.emoticon {
|
||||
.title {
|
||||
width: 50%;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
color: #ccc;
|
||||
font-weight: 400;
|
||||
padding-left: 3px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.emoticon-item {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: 2px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.3);
|
||||
}
|
||||
}
|
||||
|
||||
.symbol {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-box {
|
||||
.emoji-item {
|
||||
width: 67px;
|
||||
height: 67px;
|
||||
margin: 2px;
|
||||
background-color: #eff1f7;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
transition: ease-in 0.3s;
|
||||
}
|
||||
|
||||
.custom-emoji {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
font-size: 10px;
|
||||
|
||||
i {
|
||||
font-size: 30px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: ease-in 0.3s;
|
||||
}
|
||||
|
||||
.emoji-box .emoji-item:hover .el-image,
|
||||
.emoji-box .emoji-item:hover {
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,440 +0,0 @@
|
||||
<template>
|
||||
<el-container
|
||||
class="container animated bounceInUp"
|
||||
v-outside="closeBox"
|
||||
v-if="show"
|
||||
>
|
||||
<el-header class="no-padding header" height="50px">
|
||||
<p>
|
||||
上传管理 <span v-show="total">({{ successNum }}/{{ total }})</span>
|
||||
</p>
|
||||
<i class="close-btn el-icon-close" @click="closeBox" />
|
||||
</el-header>
|
||||
|
||||
<el-main class="no-padding mian lum-scrollbar">
|
||||
<div class="empty-data" v-show="total == 0">
|
||||
<SvgNotData />
|
||||
<p>暂无上传文件</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="file in items"
|
||||
v-show="!file.isDelete"
|
||||
:key="file.hashName"
|
||||
class="file-item"
|
||||
>
|
||||
<div class="file-header">
|
||||
<div class="type-icon">{{ file.ext }}</div>
|
||||
<el-tooltip :content="file.filename" placement="top-start">
|
||||
<div class="filename">{{ file.filename }}</div>
|
||||
</el-tooltip>
|
||||
|
||||
<div class="status">
|
||||
<span v-if="file.status == 0">等待上传</span>
|
||||
<span v-else-if="file.status == 1" style="color: #66b1ff">
|
||||
正在上传...
|
||||
</span>
|
||||
<span v-else-if="file.status == 2" style="color: #67c23a">
|
||||
已完成
|
||||
</span>
|
||||
<span v-else style="color: red">网络异常</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-mian">
|
||||
<div class="progress">
|
||||
<el-progress
|
||||
type="dashboard"
|
||||
:percentage="file.progress"
|
||||
:width="50"
|
||||
:color="colors"
|
||||
/>
|
||||
<span class="name">上传进度</span>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<p>
|
||||
文件类型:<span>{{ file.filetype }}</span>
|
||||
</p>
|
||||
<p>
|
||||
文件大小:<span>{{ file.filesize }}</span>
|
||||
</p>
|
||||
<p>
|
||||
上传时间:<span>{{ file.datetime }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="file.status == 2 || file.status == 3" class="file-means">
|
||||
<div class="btns" @click="removeFile(file.hashName)">删除</div>
|
||||
<div
|
||||
v-show="file.status == 3"
|
||||
class="btns"
|
||||
@click="triggerUpload(file.hashName)"
|
||||
>
|
||||
继续上传
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import { SvgNotData } from '@/core/icons'
|
||||
import { Progress } from 'element-ui'
|
||||
Vue.use(Progress)
|
||||
|
||||
import { ServeFindFileSplitInfo, ServeFileSubareaUpload } from '@/api/upload'
|
||||
import { formateSize, getFileExt, parseTime } from '@/utils/functions'
|
||||
import { ServeSendTalkFile } from '@/api/chat'
|
||||
|
||||
export default {
|
||||
name: 'MeEditorFileManage',
|
||||
model: {
|
||||
prop: 'show',
|
||||
event: 'close',
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
components: {
|
||||
SvgNotData,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
colors: [
|
||||
{
|
||||
color: '#f56c6c',
|
||||
percentage: 20,
|
||||
},
|
||||
{
|
||||
color: '#e6a23c',
|
||||
percentage: 40,
|
||||
},
|
||||
{
|
||||
color: '#5cb87a',
|
||||
percentage: 60,
|
||||
},
|
||||
{
|
||||
color: '#1989fa',
|
||||
percentage: 80,
|
||||
},
|
||||
{
|
||||
color: '#11ce65',
|
||||
percentage: 100,
|
||||
},
|
||||
],
|
||||
|
||||
items: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
total() {
|
||||
return this.items.filter(item => {
|
||||
return item.isDelete === false
|
||||
}).length
|
||||
},
|
||||
successNum() {
|
||||
return this.items.filter(item => {
|
||||
return item.isDelete === false && item.status == 2
|
||||
}).length
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
closeBox() {
|
||||
this.$emit('close', false)
|
||||
},
|
||||
|
||||
upload(file) {
|
||||
ServeFindFileSplitInfo({
|
||||
file_name: file.name,
|
||||
file_size: file.size,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
const { hash_name, split_size } = res.data
|
||||
|
||||
this.items.unshift({
|
||||
hashName: hash_name,
|
||||
originalFile: file,
|
||||
filename: file.name,
|
||||
status: 0, // 文件上传状态 0:等待上传 1:上传中 2:上传完成 3:网络异常
|
||||
progress: 0,
|
||||
filesize: formateSize(file.size),
|
||||
filetype: file.type || '未知',
|
||||
datetime: parseTime(new Date(), '{m}-{d} {h}:{i}'),
|
||||
ext: getFileExt(file.name),
|
||||
forms: this.fileSlice(file, hash_name, split_size),
|
||||
successNum: 0,
|
||||
isDelete: false,
|
||||
})
|
||||
|
||||
this.triggerUpload(hash_name)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 处理拆分上传文件
|
||||
fileSlice(file, hash, eachSize) {
|
||||
const ext = getFileExt(file.name)
|
||||
const splitNum = Math.ceil(file.size / eachSize) // 分片总数
|
||||
const forms = []
|
||||
|
||||
// 处理每个分片的上传操作
|
||||
for (let i = 0; i < splitNum; i++) {
|
||||
let start = i * eachSize
|
||||
let end = Math.min(file.size, start + eachSize)
|
||||
|
||||
// 构建表单
|
||||
const form = new FormData()
|
||||
form.append('file', file.slice(start, end))
|
||||
form.append('name', file.name)
|
||||
form.append('hash', hash)
|
||||
form.append('ext', ext)
|
||||
form.append('size', file.size)
|
||||
form.append('split_index', i)
|
||||
form.append('split_num', splitNum)
|
||||
forms.push(form)
|
||||
}
|
||||
|
||||
return forms
|
||||
},
|
||||
|
||||
// 触发上传文件
|
||||
triggerUpload(hashName) {
|
||||
let $index = this.getFileIndex(hashName)
|
||||
if ($index < 0 || this.items[$index].isDelte) {
|
||||
return
|
||||
}
|
||||
|
||||
let i = this.items[$index].successNum
|
||||
let form = this.items[$index].forms[i]
|
||||
let length = this.items[$index].forms.length
|
||||
this.items[$index].status = 1
|
||||
ServeFileSubareaUpload(form)
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
$index = this.getFileIndex(hashName)
|
||||
this.items[$index].successNum++
|
||||
this.items[$index].progress = Math.floor(
|
||||
(this.items[$index].successNum / length) * 100
|
||||
)
|
||||
if (this.items[$index].successNum == length) {
|
||||
this.items[$index].status = 2
|
||||
if (res.data.is_file_merge) {
|
||||
ServeSendTalkFile({
|
||||
hash_name: res.data.hash,
|
||||
receive_id: this.$store.state.dialogue.receive_id,
|
||||
source: this.$store.state.dialogue.source,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
this.triggerUpload(hashName)
|
||||
}
|
||||
} else {
|
||||
this.items[$index].status = 3
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
$index = this.getFileIndex(hashName)
|
||||
this.items[$index].status = 3
|
||||
})
|
||||
},
|
||||
|
||||
// 获取分片文件数组索引
|
||||
getFileIndex(hashName) {
|
||||
return this.items.findIndex(item => {
|
||||
return item.hashName === hashName
|
||||
})
|
||||
},
|
||||
|
||||
removeFile(hashName) {
|
||||
let index = this.getFileIndex(hashName)
|
||||
this.items[index].isDelete = true
|
||||
},
|
||||
|
||||
clear() {
|
||||
this.items = []
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 400px;
|
||||
height: 600px;
|
||||
background-color: white;
|
||||
box-shadow: 0 0 5px #eae5e5;
|
||||
border: 1px solid #eae5e5;
|
||||
overflow: hidden;
|
||||
border-radius: 3px 3px 0 0;
|
||||
|
||||
.header {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
position: relative;
|
||||
text-indent: 20px;
|
||||
border-bottom: 1px solid #f5eeee;
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 15px;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.mian {
|
||||
.empty-data {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin-top: 50%;
|
||||
|
||||
svg {
|
||||
font-size: 70px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 30px;
|
||||
color: #cccccc;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-item {
|
||||
width: 95%;
|
||||
min-height: 100px;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 5px;
|
||||
margin: 15px auto;
|
||||
box-shadow: 0 0 5px #eae5e5;
|
||||
overflow: hidden;
|
||||
|
||||
.file-header {
|
||||
height: 45px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
border-bottom: 1px solid #f7f4f4;
|
||||
|
||||
.type-icon {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
background-color: #66b1ff;
|
||||
border-radius: 50%;
|
||||
margin-left: 5px;
|
||||
font-size: 10px;
|
||||
font-weight: 200;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.filename {
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
width: 65%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 12px;
|
||||
font-size: 13px;
|
||||
color: #6b6868;
|
||||
font-weight: 200;
|
||||
}
|
||||
}
|
||||
|
||||
.file-mian {
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.progress {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
flex-shrink: 0;
|
||||
background: #f9f6f6;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
|
||||
.name {
|
||||
font-size: 12px;
|
||||
color: #ada8a8;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
||||
.detail {
|
||||
flex: auto;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 20px;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
font-size: 13px;
|
||||
|
||||
p {
|
||||
margin: 3px;
|
||||
color: #ada8a8;
|
||||
|
||||
span {
|
||||
color: #595a5a;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-means {
|
||||
width: 96.5%;
|
||||
height: 35px;
|
||||
border-top: 1px dashed rgb(234, 227, 227);
|
||||
margin: 3px auto;
|
||||
padding-top: 5px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
.btns {
|
||||
width: 80px;
|
||||
height: 25px;
|
||||
border: 1px solid #e6e1e1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 3px;
|
||||
border-radius: 15px;
|
||||
font-size: 12px;
|
||||
color: #635f5f;
|
||||
cursor: pointer;
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 0 5px #eae5e5;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,133 +0,0 @@
|
||||
<template>
|
||||
<div v-if="show" class="lum-dialog-mask animated fadeIn">
|
||||
<el-container class="lum-dialog-box" v-outside="closeBox">
|
||||
<el-header class="no-padding header" height="50px">
|
||||
<p>发送图片</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="closeBox" />
|
||||
</p>
|
||||
</el-header>
|
||||
|
||||
<el-main class="no-padding mian">
|
||||
<img v-show="src" :src="src" />
|
||||
<div v-show="src">
|
||||
<span class="filename">{{ fileName }}</span>
|
||||
<br />
|
||||
<span class="size">{{ fileSize }} KB</span>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
<el-footer class="footer" height="50px">
|
||||
<el-button
|
||||
class="btn"
|
||||
type="primary"
|
||||
size="medium"
|
||||
:loading="loading"
|
||||
@click="uploadImage"
|
||||
>立即发送
|
||||
</el-button>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'MeEditorImageView',
|
||||
model: {
|
||||
prop: 'show',
|
||||
event: 'close',
|
||||
},
|
||||
props: {
|
||||
show: Boolean,
|
||||
file: File,
|
||||
},
|
||||
watch: {
|
||||
file(file) {
|
||||
this.loadFile(file)
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
src: '',
|
||||
fileSize: '',
|
||||
fileName: '',
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeBox() {
|
||||
if (this.loading) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.$emit('close', false)
|
||||
},
|
||||
loadFile(file) {
|
||||
let reader = new FileReader()
|
||||
this.fileSize = Math.ceil(file.size / 1024)
|
||||
this.fileName = file.name
|
||||
reader.onload = () => {
|
||||
this.src = reader.result
|
||||
}
|
||||
|
||||
reader.readAsDataURL(file)
|
||||
},
|
||||
|
||||
// 确认按钮事件
|
||||
uploadImage() {
|
||||
this.loading = true
|
||||
this.$emit('confirm')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 500px;
|
||||
max-width: 500px;
|
||||
height: 450px;
|
||||
|
||||
.mian {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
img {
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 8px #e0dbdb;
|
||||
}
|
||||
|
||||
div {
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
max-width: 80%;
|
||||
|
||||
.filename {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.size {
|
||||
color: rgb(148, 140, 140);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 50px;
|
||||
background: rgba(247, 245, 245, 0.66);
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
|
||||
.btn {
|
||||
width: 150px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,518 +0,0 @@
|
||||
<template>
|
||||
<div class="lum-dialog-mask animated fadeIn">
|
||||
<el-container class="lum-dialog-box">
|
||||
<el-header class="no-padding header no-select" height="50px">
|
||||
<p>语音消息</p>
|
||||
<p class="tools"><i class="el-icon-close" @click="closeBox" /></p>
|
||||
</el-header>
|
||||
|
||||
<el-main class="no-padding mian">
|
||||
<div class="music">
|
||||
<span class="line line1" :class="{ 'line-ani': animation }"></span>
|
||||
<span class="line line2" :class="{ 'line-ani': animation }"></span>
|
||||
<span class="line line3" :class="{ 'line-ani': animation }"></span>
|
||||
<span class="line line4" :class="{ 'line-ani': animation }"></span>
|
||||
<span class="line line5" :class="{ 'line-ani': animation }"></span>
|
||||
</div>
|
||||
<div style="margin-top: 35px; color: #676262; font-weight: 300">
|
||||
<template v-if="recorderStatus == 0">
|
||||
<p style="font-size: 13px; margin-top: 5px">
|
||||
<span>语音消息,让聊天更简单方便 ...</span>
|
||||
</p>
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
recorderStatus == 1 || recorderStatus == 2 || recorderStatus == 3
|
||||
"
|
||||
>
|
||||
<p>{{ datetime }}</p>
|
||||
<p style="font-size: 13px; margin-top: 5px">
|
||||
<span v-if="recorderStatus == 1">正在录音</span>
|
||||
<span v-else-if="recorderStatus == 2">已暂停录音</span>
|
||||
<span v-else-if="recorderStatus == 3">录音时长</span>
|
||||
</p>
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
recorderStatus == 4 || recorderStatus == 5 || recorderStatus == 6
|
||||
"
|
||||
>
|
||||
<p>{{ formatPlayTime }}</p>
|
||||
<p style="font-size: 13px; margin-top: 5px">
|
||||
<span v-if="recorderStatus == 4">正在播放</span>
|
||||
<span v-else-if="recorderStatus == 5">已暂停播放</span>
|
||||
<span v-else-if="recorderStatus == 6">播放已结束</span>
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
<el-footer class="footer" height="50px">
|
||||
<!-- 0:未开始录音 1:正在录音 2:暂停录音 3:结束录音 4:播放录音 5:停止播放 -->
|
||||
<el-button
|
||||
v-show="recorderStatus == 0"
|
||||
type="primary"
|
||||
size="mini"
|
||||
round
|
||||
icon="el-icon-microphone"
|
||||
@click="startRecorder"
|
||||
>开始录音
|
||||
</el-button>
|
||||
<el-button
|
||||
v-show="recorderStatus == 1"
|
||||
type="primary"
|
||||
size="mini"
|
||||
round
|
||||
icon="el-icon-video-pause"
|
||||
@click="pauseRecorder"
|
||||
>暂停录音
|
||||
</el-button>
|
||||
<el-button
|
||||
v-show="recorderStatus == 2"
|
||||
type="primary"
|
||||
size="mini"
|
||||
round
|
||||
icon="el-icon-microphone"
|
||||
@click="resumeRecorder"
|
||||
>继续录音
|
||||
</el-button>
|
||||
<el-button
|
||||
v-show="recorderStatus == 2"
|
||||
type="primary"
|
||||
size="mini"
|
||||
round
|
||||
icon="el-icon-microphone"
|
||||
@click="stopRecorder"
|
||||
>结束录音
|
||||
</el-button>
|
||||
<el-button
|
||||
v-show="recorderStatus == 3 || recorderStatus == 6"
|
||||
type="primary"
|
||||
size="mini"
|
||||
round
|
||||
icon="el-icon-video-play"
|
||||
@click="playRecorder"
|
||||
>播放录音
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
v-show="
|
||||
recorderStatus == 3 || recorderStatus == 5 || recorderStatus == 6
|
||||
"
|
||||
type="primary"
|
||||
size="mini"
|
||||
round
|
||||
icon="el-icon-video-play"
|
||||
@click="startRecorder"
|
||||
>重新录音
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
v-show="recorderStatus == 4"
|
||||
type="primary"
|
||||
size="mini"
|
||||
round
|
||||
icon="el-icon-video-pause"
|
||||
@click="pausePlayRecorder"
|
||||
>暂停播放
|
||||
</el-button>
|
||||
<el-button
|
||||
v-show="recorderStatus == 5"
|
||||
type="primary"
|
||||
size="mini"
|
||||
round
|
||||
icon="el-icon-video-play"
|
||||
@click="resumePlayRecorder"
|
||||
>继续播放
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
v-show="
|
||||
recorderStatus == 3 || recorderStatus == 5 || recorderStatus == 6
|
||||
"
|
||||
type="primary"
|
||||
size="mini"
|
||||
round
|
||||
@click="submit"
|
||||
>立即发送
|
||||
</el-button>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Recorder from 'js-audio-recorder'
|
||||
|
||||
export default {
|
||||
name: 'MeEditorRecorder',
|
||||
data() {
|
||||
return {
|
||||
// 录音实例
|
||||
recorder: null,
|
||||
|
||||
// 录音时长
|
||||
duration: 0,
|
||||
|
||||
// 播放时长
|
||||
playTime: 0,
|
||||
|
||||
animation: false,
|
||||
|
||||
// 当前状态
|
||||
recorderStatus: 0, //0:未开始录音 1:正在录音 2:暂停录音 3:结束录音 4:播放录音 5:停止播放 6:播放结束
|
||||
|
||||
playTimeout: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
datetime() {
|
||||
let hour = parseInt((this.duration / 60 / 60) % 24) //小时
|
||||
let minute = parseInt((this.duration / 60) % 60) //分钟
|
||||
let seconds = parseInt(this.duration % 60) //秒
|
||||
|
||||
if (hour < 10) hour = `0${hour}`
|
||||
if (minute < 10) minute = `0${minute}`
|
||||
if (seconds < 10) seconds = `0${seconds}`
|
||||
|
||||
return `${hour}:${minute}:${seconds}`
|
||||
},
|
||||
formatPlayTime() {
|
||||
let hour = parseInt((this.playTime / 60 / 60) % 24) //小时
|
||||
let minute = parseInt((this.playTime / 60) % 60) //分钟
|
||||
let seconds = parseInt(this.playTime % 60) //秒
|
||||
|
||||
if (hour < 10) hour = `0${hour}`
|
||||
if (minute < 10) minute = `0${minute}`
|
||||
if (seconds < 10) seconds = `0${seconds}`
|
||||
|
||||
return `${hour}:${minute}:${seconds}`
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
if (this.recorder) {
|
||||
this.destroyRecorder()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeBox() {
|
||||
if (this.recorder == null) {
|
||||
this.$emit('close', false)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.recorderStatus == 1) {
|
||||
this.stopRecorder()
|
||||
} else if (this.recorderStatus == 4) {
|
||||
this.pausePlayRecorder()
|
||||
}
|
||||
|
||||
// 销毁录音后关闭窗口
|
||||
this.destroyRecorder(() => {
|
||||
this.$emit('close', false)
|
||||
})
|
||||
},
|
||||
|
||||
// 开始录音
|
||||
startRecorder() {
|
||||
let _this = this
|
||||
// http://recorder.api.zhuyuntao.cn/Recorder/event.html
|
||||
// https://blog.csdn.net/qq_41619796/article/details/107865602
|
||||
this.recorder = new Recorder()
|
||||
this.recorder.onprocess = duration => {
|
||||
duration = parseInt(duration)
|
||||
_this.duration = duration
|
||||
}
|
||||
|
||||
this.recorder.start().then(
|
||||
() => {
|
||||
this.recorderStatus = 1
|
||||
this.animation = true
|
||||
},
|
||||
error => {
|
||||
console.log(`${error.name} : ${error.message}`)
|
||||
}
|
||||
)
|
||||
},
|
||||
// 暂停录音
|
||||
pauseRecorder() {
|
||||
this.recorder.pause()
|
||||
this.recorderStatus = 2
|
||||
this.animation = false
|
||||
},
|
||||
// 继续录音
|
||||
resumeRecorder() {
|
||||
this.recorderStatus = 1
|
||||
this.recorder.resume()
|
||||
this.animation = true
|
||||
},
|
||||
// 结束录音
|
||||
stopRecorder() {
|
||||
this.recorderStatus = 3
|
||||
this.recorder.stop()
|
||||
this.animation = false
|
||||
},
|
||||
// 录音播放
|
||||
playRecorder() {
|
||||
this.recorderStatus = 4
|
||||
this.recorder.play()
|
||||
this.playTimeouts()
|
||||
this.animation = true
|
||||
},
|
||||
// 暂停录音播放
|
||||
pausePlayRecorder() {
|
||||
this.recorderStatus = 5
|
||||
this.recorder.pausePlay()
|
||||
clearInterval(this.playTimeout)
|
||||
this.animation = false
|
||||
},
|
||||
// 恢复录音播放
|
||||
resumePlayRecorder() {
|
||||
this.recorderStatus = 4
|
||||
this.recorder.resumePlay()
|
||||
this.playTimeouts()
|
||||
this.animation = true
|
||||
},
|
||||
// 销毁录音
|
||||
destroyRecorder(callBack) {
|
||||
this.recorder.destroy().then(() => {
|
||||
this.recorder = null
|
||||
console.log('销毁了...')
|
||||
if (callBack) {
|
||||
callBack()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取录音文件大小(单位:字节)
|
||||
recorderSize() {
|
||||
return this.recorder.fileSize
|
||||
},
|
||||
|
||||
playTimeouts() {
|
||||
this.playTimeout = setInterval(() => {
|
||||
let time = parseInt(this.recorder.getPlayTime())
|
||||
this.playTime = time
|
||||
if (time == this.duration) {
|
||||
clearInterval(this.playTimeout)
|
||||
this.animation = false
|
||||
this.recorderStatus = 6
|
||||
}
|
||||
}, 100)
|
||||
},
|
||||
|
||||
submit() {
|
||||
alert('功能研发中,敬请期待...')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 500px;
|
||||
max-width: 500px;
|
||||
height: 450px;
|
||||
|
||||
.mian {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
border-top: 1px solid #f7f3f3;
|
||||
}
|
||||
}
|
||||
|
||||
.music {
|
||||
position: relative;
|
||||
width: 180px;
|
||||
height: 160px;
|
||||
border: 8px solid #bebebe;
|
||||
border-bottom: 0px;
|
||||
border-top-left-radius: 110px;
|
||||
border-top-right-radius: 110px;
|
||||
}
|
||||
|
||||
.music:before,
|
||||
.music:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -20px;
|
||||
width: 40px;
|
||||
height: 82px;
|
||||
background-color: #bebebe;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.music:before {
|
||||
right: -25px;
|
||||
}
|
||||
|
||||
.music:after {
|
||||
left: -25px;
|
||||
}
|
||||
|
||||
.line {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
min-height: 30px;
|
||||
transition: 0.5s;
|
||||
|
||||
vertical-align: middle;
|
||||
bottom: 0 !important;
|
||||
box-shadow: inset 0px 0px 16px -2px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.line-ani {
|
||||
animation: equalize 4s 0s infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.line1 {
|
||||
left: 30%;
|
||||
bottom: 0px;
|
||||
animation-delay: -1.9s;
|
||||
background-color: #ff5e50;
|
||||
}
|
||||
|
||||
.line2 {
|
||||
left: 40%;
|
||||
height: 60px;
|
||||
bottom: -15px;
|
||||
animation-delay: -2.9s;
|
||||
background-color: #a64de6;
|
||||
}
|
||||
|
||||
.line3 {
|
||||
left: 50%;
|
||||
height: 30px;
|
||||
bottom: -1.5px;
|
||||
animation-delay: -3.9s;
|
||||
background-color: #5968dc;
|
||||
}
|
||||
|
||||
.line4 {
|
||||
left: 60%;
|
||||
height: 65px;
|
||||
bottom: -16px;
|
||||
animation-delay: -4.9s;
|
||||
background-color: #27c8f8;
|
||||
}
|
||||
|
||||
.line5 {
|
||||
left: 70%;
|
||||
height: 60px;
|
||||
bottom: -12px;
|
||||
animation-delay: -5.9s;
|
||||
background-color: #cc60b5;
|
||||
}
|
||||
|
||||
@keyframes equalize {
|
||||
0% {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
4% {
|
||||
height: 42px;
|
||||
}
|
||||
|
||||
8% {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
12% {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
16% {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
20% {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
24% {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
28% {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
32% {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
36% {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
40% {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
44% {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
48% {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
52% {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
56% {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
60% {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
64% {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
68% {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
72% {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
76% {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
80% {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
84% {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
88% {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
92% {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
96% {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
100% {
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,169 +0,0 @@
|
||||
<template>
|
||||
<div class="lum-dialog-mask">
|
||||
<el-container class="lum-dialog-box" v-outside="closeBox">
|
||||
<el-header class="no-padding header" height="50px">
|
||||
<p>系统表情</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="closeBox" />
|
||||
</p>
|
||||
</el-header>
|
||||
|
||||
<el-main class="no-padding mian lum-scrollbar">
|
||||
<ul>
|
||||
<li v-for="(item, i) in items" :key="item.id" class="no-select">
|
||||
<div class="pkg-avatar">
|
||||
<el-image :src="item.url" fit="cover" :lazy="true" />
|
||||
</div>
|
||||
<div class="pkg-info" v-text="item.name"></div>
|
||||
<div class="pkg-status">
|
||||
<button
|
||||
:class="{
|
||||
'add-emoji': item.status == 0,
|
||||
'remove-emoji': item.status != 0,
|
||||
}"
|
||||
@click="setEmoticon(i, item, item.status == 0 ? 1 : 2)"
|
||||
>
|
||||
{{ item.status == 0 ? '添加' : '移除' }}
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</el-main>
|
||||
|
||||
<el-footer class="footer" height="50px">
|
||||
<el-button type="primary" size="medium" class="btn" @click="closeBox">
|
||||
关闭窗口
|
||||
</el-button>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ServeFindSysEmoticon, ServeSetUserEmoticon } from '@/api/emoticon'
|
||||
|
||||
export default {
|
||||
name: 'MeEditorSystemEmoticon',
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadSysEmoticon()
|
||||
},
|
||||
methods: {
|
||||
closeBox() {
|
||||
this.$emit('close')
|
||||
},
|
||||
|
||||
// 获取系统表情包列表
|
||||
loadSysEmoticon() {
|
||||
ServeFindSysEmoticon().then(res => {
|
||||
if (res.code == 200) {
|
||||
this.items = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
setEmoticon(index, item, type) {
|
||||
ServeSetUserEmoticon({
|
||||
emoticon_id: item.id,
|
||||
type: type,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
if (type == 1) {
|
||||
this.items[index].status = 1
|
||||
this.$store.commit('APPEND_SYS_EMOTICON', res.data)
|
||||
} else {
|
||||
this.items[index].status = 0
|
||||
this.$store.commit('REMOVE_SYS_EMOTICON', {
|
||||
emoticon_id: item.id,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 350px;
|
||||
max-width: 350px;
|
||||
height: 500px;
|
||||
|
||||
.mian {
|
||||
height: 480px;
|
||||
overflow-y: auto;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
height: 68px;
|
||||
align-items: center;
|
||||
border-bottom: 3px solid #fbf2fb;
|
||||
padding-left: 5px;
|
||||
|
||||
.pkg-avatar {
|
||||
flex-shrink: 0;
|
||||
|
||||
.el-image {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-left: 14px;
|
||||
margin-right: 14px;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
width: 200px;
|
||||
color: #615d5d;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pkg-status {
|
||||
flex-shrink: 0;
|
||||
|
||||
button {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
line-height: 28px;
|
||||
border-radius: 20px;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.add-emoji {
|
||||
background-color: #38adff;
|
||||
}
|
||||
|
||||
.remove-emoji {
|
||||
background-color: #ff5722;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 50px;
|
||||
background: rgba(247, 245, 245, 0.66);
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
|
||||
.btn {
|
||||
width: 150px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<div class="empty-content">
|
||||
<div class="image">
|
||||
<img :src="src" />
|
||||
</div>
|
||||
<div class="text" v-text="text" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Empty',
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
default: '数据为空...',
|
||||
},
|
||||
src: {
|
||||
type: String,
|
||||
default: require('@/assets/image/no-oncall.6b776fcf.png'),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.empty-content {
|
||||
width: 100%;
|
||||
height: 60%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
|
||||
.image {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,313 +0,0 @@
|
||||
<template>
|
||||
<div class="loading-content">
|
||||
<div class="ant-spin ant-spin-lg ant-spin-spinning">
|
||||
<span class="ant-spin-dot ant-spin-dot-spin">
|
||||
<i class="ant-spin-dot-item" />
|
||||
<i class="ant-spin-dot-item" />
|
||||
<i class="ant-spin-dot-item" />
|
||||
<i class="ant-spin-dot-item" />
|
||||
</span>
|
||||
</div>
|
||||
<p>{{ text }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Loading',
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
default: '数据加载中 ...',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.loading-content {
|
||||
width: 100%;
|
||||
height: 60%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ant-spin 加载动画 start */
|
||||
.ant-spin {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
font-size: 14px;
|
||||
font-variant: tabular-nums;
|
||||
line-height: 1.5715;
|
||||
list-style: none;
|
||||
-webkit-font-feature-settings: 'tnum';
|
||||
font-feature-settings: 'tnum';
|
||||
position: absolute;
|
||||
display: none;
|
||||
color: #1890ff;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
opacity: 0;
|
||||
-webkit-transition: -webkit-transform 0.3s
|
||||
cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
|
||||
-webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
}
|
||||
|
||||
.ant-spin-spinning {
|
||||
position: static;
|
||||
display: inline-block;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 4;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin .ant-spin-dot {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -10px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin .ant-spin-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
padding-top: 5px;
|
||||
text-shadow: 0 1px 2px #fff;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin.ant-spin-show-text .ant-spin-dot {
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-sm .ant-spin-dot {
|
||||
margin: -7px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-sm .ant-spin-text {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-sm.ant-spin-show-text .ant-spin-dot {
|
||||
margin-top: -17px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-lg .ant-spin-dot {
|
||||
margin: -16px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-lg .ant-spin-text {
|
||||
padding-top: 11px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-lg.ant-spin-show-text .ant-spin-dot {
|
||||
margin-top: -26px;
|
||||
}
|
||||
|
||||
.ant-spin-container {
|
||||
position: relative;
|
||||
-webkit-transition: opacity 0.3s;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.ant-spin-container::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
display: none \9;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
opacity: 0;
|
||||
-webkit-transition: all 0.3s;
|
||||
transition: all 0.3s;
|
||||
content: '';
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ant-spin-blur {
|
||||
clear: both;
|
||||
overflow: hidden;
|
||||
opacity: 0.5;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ant-spin-blur::after {
|
||||
opacity: 0.4;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.ant-spin-tip {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.ant-spin-dot {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
background-color: #1890ff;
|
||||
border-radius: 100%;
|
||||
-webkit-transform: scale(0.75);
|
||||
transform: scale(0.75);
|
||||
-webkit-transform-origin: 50% 50%;
|
||||
transform-origin: 50% 50%;
|
||||
opacity: 0.3;
|
||||
-webkit-animation: antSpinMove 1s infinite linear alternate;
|
||||
animation: antSpinMove 1s infinite linear alternate;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item:nth-child(1) {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item:nth-child(2) {
|
||||
top: 0;
|
||||
right: 0;
|
||||
-webkit-animation-delay: 0.4s;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item:nth-child(3) {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
-webkit-animation-delay: 0.8s;
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item:nth-child(4) {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
-webkit-animation-delay: 1.2s;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
|
||||
.ant-spin-dot-spin {
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
-webkit-animation: antRotate 1.2s infinite linear;
|
||||
animation: antRotate 1.2s infinite linear;
|
||||
}
|
||||
|
||||
.ant-spin-sm .ant-spin-dot {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ant-spin-sm .ant-spin-dot i {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.ant-spin-lg .ant-spin-dot {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.ant-spin-lg .ant-spin-dot i {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.ant-spin.ant-spin-show-text .ant-spin-text {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
||||
.ant-spin-blur {
|
||||
background: #fff;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes antSpinMove {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antSpinMove {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes antRotate {
|
||||
to {
|
||||
-webkit-transform: rotate(405deg);
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antRotate {
|
||||
to {
|
||||
-webkit-transform: rotate(405deg);
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-spin-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.ant-spin-rtl .ant-spin-dot-spin {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
-webkit-animation-name: antRotateRtl;
|
||||
animation-name: antRotateRtl;
|
||||
}
|
||||
|
||||
@-webkit-keyframes antRotateRtl {
|
||||
to {
|
||||
-webkit-transform: rotate(-405deg);
|
||||
transform: rotate(-405deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antRotateRtl {
|
||||
to {
|
||||
-webkit-transform: rotate(-405deg);
|
||||
transform: rotate(-405deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* ant-spin 加载动画 end */
|
||||
</style>
|
||||
@@ -1,448 +0,0 @@
|
||||
<template>
|
||||
<div class="lum-dialog-mask">
|
||||
<el-container class="lum-dialog-box" v-outside="close">
|
||||
<el-header class="no-padding header no-select" height="60px">
|
||||
<p>
|
||||
{{ from.groupId == 0 ? '创建群组' : '请选择需要邀请的好友' }}
|
||||
</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="close" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main no-padding">
|
||||
<el-container class="full-height">
|
||||
<el-aside width="250px" class="aside-border">
|
||||
<el-container class="full-height no-select">
|
||||
<el-header
|
||||
class="no-padding search-header"
|
||||
height="50px"
|
||||
:class="{ shadow: searchHeaderShadow }"
|
||||
>
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
placeholder="搜索 | 好友 or 群组"
|
||||
prefix-icon="el-icon-search"
|
||||
clearable
|
||||
size="small"
|
||||
/>
|
||||
</el-header>
|
||||
<el-main class="no-padding">
|
||||
<el-scrollbar
|
||||
ref="scrollbar"
|
||||
class="full-height"
|
||||
tag="section"
|
||||
:native="false"
|
||||
>
|
||||
<ul class="friend-items no-select">
|
||||
<li
|
||||
v-for="item in search"
|
||||
:key="item.id"
|
||||
@click="triggerContacts(item)"
|
||||
>
|
||||
<el-avatar
|
||||
class="avatar"
|
||||
style="margin-top: 5px"
|
||||
:size="25"
|
||||
:src="item.avatar"
|
||||
>
|
||||
<img src="~@/assets/image/detault-avatar.jpg" />
|
||||
</el-avatar>
|
||||
<span class="nickname">{{ item.nickname }}</span>
|
||||
<span class="select-btn">
|
||||
<i
|
||||
class="el-icon-success"
|
||||
:class="{ 'icon-active': item.checked }"
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-aside>
|
||||
|
||||
<el-main class="no-padding">
|
||||
<el-container class="full-height">
|
||||
<el-header height="50px" v-show="!readonly">
|
||||
<div class="group-from no-select">
|
||||
<label>群名称</label>
|
||||
<p>
|
||||
<el-input
|
||||
v-model="from.groupName"
|
||||
placeholder="请输入群名称(必填)"
|
||||
size="small"
|
||||
:maxlength="20"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-header height="40px" :class="{ mt40: !readonly }">
|
||||
<el-divider content-position="left" class="no-select">
|
||||
<span style="color: #c4c5c7">
|
||||
邀请成员 ({{ selected.length }})
|
||||
</span>
|
||||
</el-divider>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<el-scrollbar :native="false" tag="section" class="full-height">
|
||||
<div class="selectd-items">
|
||||
<div
|
||||
v-for="item in selected"
|
||||
:key="item.id"
|
||||
class="selectd-item no-select"
|
||||
>
|
||||
<el-avatar :size="25" :src="item.avatar" />
|
||||
<p>{{ item.nickname }}</p>
|
||||
<div class="triangle-topleft"></div>
|
||||
<div class="del-mask" @click="delContacts(item)">
|
||||
<i class="el-icon-delete" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
<el-footer height="50px" class="no-padding footer">
|
||||
<el-button size="small" @click="close" plain>取消</el-button>
|
||||
<el-button
|
||||
v-if="from.groupId == 0"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="createSubmit"
|
||||
>
|
||||
创建群组<span v-show="selected.length">({{ selected.length }})</span>
|
||||
</el-button>
|
||||
<el-button v-else type="primary" size="small" @click="inviteSubmit">
|
||||
立即邀请({{ selected.length }})
|
||||
</el-button>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
ServeCreateGroup,
|
||||
ServeInviteGroup,
|
||||
ServeGetInviteFriends,
|
||||
} from '@/api/group'
|
||||
|
||||
export default {
|
||||
name: 'GroupLaunch',
|
||||
props: {
|
||||
groupId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
readonly: false,
|
||||
from: {
|
||||
groupId: 0,
|
||||
groupName: '',
|
||||
},
|
||||
contacts: [],
|
||||
search: [],
|
||||
searchHeaderShadow: false,
|
||||
keywords: '',
|
||||
isAvatarCropper: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selected() {
|
||||
return this.contacts.filter(item => item.checked)
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
keywords(val) {
|
||||
this.search =
|
||||
val == ''
|
||||
? this.contacts
|
||||
: this.contacts.filter(item => {
|
||||
return item.nickname.match(this.keywords) != null
|
||||
})
|
||||
},
|
||||
contacts(arr) {
|
||||
if (this.keywords == '') {
|
||||
this.search = arr
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.groupId > 0) {
|
||||
this.readonly = true
|
||||
this.from.groupId = this.groupId
|
||||
}
|
||||
this.friendsApi()
|
||||
},
|
||||
mounted() {
|
||||
this.handleScroll()
|
||||
},
|
||||
methods: {
|
||||
// 触发选择联系人事件
|
||||
triggerContacts(item) {
|
||||
let index = this.contacts.findIndex(val => {
|
||||
return val.id === item.id
|
||||
})
|
||||
|
||||
this.contacts[index].checked = !this.contacts[index].checked
|
||||
},
|
||||
|
||||
// 取消选中的联系人
|
||||
delContacts(item) {
|
||||
let index = this.contacts.findIndex(val => {
|
||||
return val.id === item.id
|
||||
})
|
||||
|
||||
this.contacts[index].checked = false
|
||||
},
|
||||
|
||||
// 移除所有选中选项
|
||||
delAll() {
|
||||
this.contacts.forEach((item, i) => {
|
||||
this.contacts[i].checked = false
|
||||
})
|
||||
},
|
||||
|
||||
// 关闭窗口
|
||||
close() {
|
||||
this.$emit('close')
|
||||
},
|
||||
|
||||
// 获取选中的ID列表
|
||||
getIds() {
|
||||
return this.selected.map(item => item.id)
|
||||
},
|
||||
|
||||
// 加载好友列表
|
||||
friendsApi() {
|
||||
ServeGetInviteFriends({
|
||||
group_id: this.from.groupId,
|
||||
}).then(res => {
|
||||
if (res.code == 200 && res.data) {
|
||||
this.contacts = []
|
||||
|
||||
let data = res.data.map(item => {
|
||||
return Object.assign(item, {
|
||||
nickname: item.friend_remark ? item.friend_remark : item.nickname,
|
||||
checked: false,
|
||||
})
|
||||
})
|
||||
|
||||
this.contacts.push(...data)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
//创建聊天群
|
||||
createSubmit() {
|
||||
let data = {
|
||||
group_avatar: '',
|
||||
group_name: this.from.groupName,
|
||||
group_profile: '',
|
||||
uids: this.getIds().join(','),
|
||||
}
|
||||
|
||||
if (data.group_name == '') {
|
||||
this.$message('群聊名称不能为空!')
|
||||
return
|
||||
}
|
||||
|
||||
if (this.getIds().length < 2) {
|
||||
this.$message('群聊人数必须大于俩人!')
|
||||
return
|
||||
}
|
||||
|
||||
ServeCreateGroup(data).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.$emit('create-success', res.data)
|
||||
} else {
|
||||
this.$message('创建群聊失败!')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
//好友邀请提交
|
||||
inviteSubmit() {
|
||||
ServeInviteGroup({
|
||||
group_id: this.from.groupId,
|
||||
uids: this.getIds().join(','),
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.$emit('invite-success')
|
||||
} else {
|
||||
this.$message('邀请好友失败!')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 滚动条监听
|
||||
handleScroll() {
|
||||
let scrollbarEl = this.$refs.scrollbar.wrap
|
||||
scrollbarEl.onscroll = () => {
|
||||
this.searchHeaderShadow = scrollbarEl.scrollTop != 0
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/deep/.el-scrollbar__wrap {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.lum-dialog-box {
|
||||
width: 650px;
|
||||
max-width: 650px;
|
||||
height: 550px;
|
||||
|
||||
.main {
|
||||
.aside-border {
|
||||
border-right: 1px solid #f5eeee;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding-right: 10px;
|
||||
border-top: 1px solid #f5eeee;
|
||||
}
|
||||
}
|
||||
|
||||
.search-header {
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.shadow {
|
||||
box-shadow: 0 2px 6px 0 rgba(31, 35, 41, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
.friend-items {
|
||||
li {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
width: 60%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 52px;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.select-btn {
|
||||
position: absolute;
|
||||
width: 32px;
|
||||
height: 35px;
|
||||
right: 3px;
|
||||
top: 10px;
|
||||
line-height: 35px;
|
||||
text-align: center;
|
||||
|
||||
i {
|
||||
color: #ccc;
|
||||
|
||||
&.icon-active {
|
||||
color: #26bcfe !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.group-from {
|
||||
label {
|
||||
height: 45px;
|
||||
line-height: 47px;
|
||||
width: 50px;
|
||||
color: #606266;
|
||||
padding-right: 3px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 25px;
|
||||
width: 100%;
|
||||
text-indent: 3px;
|
||||
color: #a9a4a4;
|
||||
font-size: 12px;
|
||||
border-bottom: 1px solid #efebeb;
|
||||
}
|
||||
}
|
||||
|
||||
.selectd-items {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.selectd-item {
|
||||
width: 23%;
|
||||
height: 65px;
|
||||
margin: 6px 2px 0px 2px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
box-shadow: 0px 0px 3px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
p {
|
||||
width: 90%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 10px;
|
||||
margin-top: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.del-mask {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(31, 35, 41, 0.5);
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
transition: ease 0.5s;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
&:hover .del-mask {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mt40 {
|
||||
margin-top: 40px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,926 +0,0 @@
|
||||
<template>
|
||||
<div class="lum-dialog-mask">
|
||||
<div class="lum-dialog-box">
|
||||
<el-container class="container">
|
||||
<el-header class="header no-select" height="60px">
|
||||
<p>群管理 ({{ detail.group_name }})</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="$emit('close')" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main no-padding">
|
||||
<el-container class="full-height">
|
||||
<el-aside width="100px" class="aside-border no-select">
|
||||
<div
|
||||
v-for="(menu, index) in menus"
|
||||
:key="menu.name"
|
||||
class="menu-list"
|
||||
:class="{ selectd: tabIndex == index }"
|
||||
v-text="menu.name"
|
||||
@click="triggerTab(index)"
|
||||
/>
|
||||
</el-aside>
|
||||
|
||||
<!-- 群介绍模块 -->
|
||||
<el-main v-if="tabIndex == 0">
|
||||
<el-row>
|
||||
<el-col :span="14">
|
||||
<el-form ref="groupForm" :model="form" :rules="rules">
|
||||
<el-form-item label="群名:" prop="group_name">
|
||||
<el-input
|
||||
v-model="form.group_name"
|
||||
size="medium"
|
||||
placeholder="请输入群名称"
|
||||
maxlength="30"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="群描述:">
|
||||
<el-input
|
||||
v-model="form.profile"
|
||||
type="textarea"
|
||||
rows="3"
|
||||
placeholder="请输入群描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-edit"
|
||||
size="small"
|
||||
:loading="loading"
|
||||
@click="editGroup"
|
||||
>修改信息
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="10" class="avatar-col">
|
||||
<div class="avatar-box">
|
||||
<img v-show="form.avatar" :src="form.avatar" />
|
||||
<div class="upload-icon">
|
||||
<i class="el-icon-upload" />
|
||||
</div>
|
||||
<div class="upload-mask" @click="isAvatarCropper = true">
|
||||
<i class="el-icon-plus" />
|
||||
</div>
|
||||
</div>
|
||||
<p style="margin-top: 20px">设置头像</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-main>
|
||||
|
||||
<!-- 群成员模块 -->
|
||||
<el-main v-else-if="tabIndex == 1" class="no-padding">
|
||||
<el-container class="full-height">
|
||||
<el-header height="50px" class="notice-header">
|
||||
<el-input
|
||||
v-model="searchMembers"
|
||||
style="width: 200px"
|
||||
size="small"
|
||||
clearable
|
||||
prefix-icon="el-icon-search"
|
||||
:placeholder="`搜索群成员 ( 共${members.length}人 )`"
|
||||
/>
|
||||
<p>
|
||||
<el-button
|
||||
plain
|
||||
size="small"
|
||||
icon="el-icon-plus"
|
||||
@click="inviteFriendBox = true"
|
||||
>邀请好友
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="batchDelMember"
|
||||
type="danger"
|
||||
size="small"
|
||||
icon="el-icon-delete"
|
||||
@click="deleteMembers"
|
||||
>确认删除
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
plain
|
||||
size="small"
|
||||
icon="el-icon-finished"
|
||||
@click="batchDelMember = true"
|
||||
>批量操作
|
||||
</el-button>
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="no-padding">
|
||||
<el-scrollbar
|
||||
class="full-height"
|
||||
tag="section"
|
||||
:native="false"
|
||||
>
|
||||
<div class="members">
|
||||
<div
|
||||
v-for="member in filterMembers"
|
||||
class="member no-select"
|
||||
:class="{
|
||||
selectd: member.is_delete && batchDelMember,
|
||||
}"
|
||||
:key="member.user_id"
|
||||
>
|
||||
<div class="item-header">
|
||||
<div class="avatar" @click="catUserDetail(member)">
|
||||
<el-avatar :size="30" :src="member.avatar">
|
||||
<img src="~@/assets/image/detault-avatar.jpg" />
|
||||
</el-avatar>
|
||||
<span class="nickname" v-text="member.nickname" />
|
||||
<span class="larkc-tag" v-show="member.leader == 2">
|
||||
群主
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-show="batchDelMember && member.leader != 2"
|
||||
class="tools"
|
||||
>
|
||||
<i
|
||||
class="el-icon-success"
|
||||
:class="{ 'is-delete': member.is_delete }"
|
||||
@click.stop="triggerDelBtn(member)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile">
|
||||
签名 | {{ member.motto ? member.motto : '未设置' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
|
||||
<!-- 群公告模块 -->
|
||||
<el-main v-else-if="tabIndex == 2" class="no-padding">
|
||||
<el-container class="full-height">
|
||||
<el-header
|
||||
class="notice-header"
|
||||
height="50px"
|
||||
style="padding-left: 14px"
|
||||
>
|
||||
<span>群公告 ({{ notice.items.length }})</span>
|
||||
<el-button
|
||||
plain
|
||||
size="small"
|
||||
icon="el-icon-plus"
|
||||
@click="showNoticeBox(0, '', '')"
|
||||
>
|
||||
添加公告
|
||||
</el-button>
|
||||
</el-header>
|
||||
|
||||
<el-main class="no-padding">
|
||||
<el-scrollbar
|
||||
class="full-height"
|
||||
tag="section"
|
||||
:native="false"
|
||||
>
|
||||
<div v-if="notice.items.length == 0" class="empty-notice">
|
||||
<SvgNotData style="width: 80px; margin-bottom: 10px" />
|
||||
<span>暂无群公告</span>
|
||||
</div>
|
||||
|
||||
<div v-else class="notices">
|
||||
<div
|
||||
v-for="(item, index) in notice.items"
|
||||
:key="item.id"
|
||||
class="notice"
|
||||
>
|
||||
<div class="title">
|
||||
<span
|
||||
class="left-title"
|
||||
v-text="item.title"
|
||||
@click="
|
||||
showNoticeBox(item.id, item.title, item.content)
|
||||
"
|
||||
></span>
|
||||
<span
|
||||
class="right-tools no-select"
|
||||
@click="catNoticeDetail(index)"
|
||||
>{{ item.isShow ? '收起' : '展开' }}</span
|
||||
>
|
||||
</div>
|
||||
<p class="datetime">
|
||||
<el-avatar :size="15" :src="item.avatar">
|
||||
<img src="~@/assets/image/detault-avatar.jpg" />
|
||||
</el-avatar>
|
||||
<span
|
||||
class="text nickname"
|
||||
v-text="item.nickname"
|
||||
@click="$refs.userBusinessCard.open(item.user_id)"
|
||||
></span>
|
||||
<span class="text">
|
||||
发表于 {{ item.created_at.substr(0, 16) }}
|
||||
</span>
|
||||
</p>
|
||||
<p
|
||||
class="content"
|
||||
:class="{ retract: !item.isShow }"
|
||||
v-text="item.content"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
|
||||
<el-main v-else-if="tabIndex == 3" class="no-padding"> </el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
|
||||
<!-- 编辑公告信息 -->
|
||||
<div class="lum-dialog-mask animated fadeIn" v-show="notice.isShowform">
|
||||
<div class="notice-box">
|
||||
<h4>编辑群公告</h4>
|
||||
<el-form ref="noticeForm" :model="notice.form" :rules="notice.rules">
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input
|
||||
v-model="notice.form.title"
|
||||
size="medium"
|
||||
placeholder="请输入标题..."
|
||||
maxlength="30"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="详情" prop="content">
|
||||
<el-input
|
||||
v-model="notice.form.content"
|
||||
type="textarea"
|
||||
rows="5"
|
||||
placeholder="请输入公告详情..."
|
||||
maxlength="500"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item style="text-align: right">
|
||||
<el-button plain size="small" @click="notice.isShowform = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="notice.loading"
|
||||
@click="onSubmitNotice"
|
||||
>提交
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<transition name="el-fade-in-linear">
|
||||
<AvatarCropper v-if="isAvatarCropper" @close="closeAvatarCropper" />
|
||||
</transition>
|
||||
|
||||
<!-- 查看好友用户信息 -->
|
||||
<UserBusinessCard ref="userBusinessCard" />
|
||||
|
||||
<transition name="el-fade-in-linear">
|
||||
<GroupLaunch
|
||||
v-if="inviteFriendBox"
|
||||
:group-id="groupId"
|
||||
@close="inviteFriendBox = false"
|
||||
@invite-success="inviteSuccess"
|
||||
/>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import AvatarCropper from '@/components/layout/AvatarCropper'
|
||||
import UserBusinessCard from '@/components/user/UserBusinessCard'
|
||||
import GroupLaunch from '@/components/group/GroupLaunch'
|
||||
import { SvgNotData } from '@/core/icons'
|
||||
import {
|
||||
ServeGetGroupMembers,
|
||||
ServeGetGroupNotices,
|
||||
ServeEditGroupNotice,
|
||||
ServeRemoveMembersGroup,
|
||||
ServeGroupDetail,
|
||||
ServeEditGroup,
|
||||
} from '@/api/group'
|
||||
|
||||
export default {
|
||||
name: 'GroupManage',
|
||||
props: {
|
||||
groupId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
AvatarCropper,
|
||||
UserBusinessCard,
|
||||
GroupLaunch,
|
||||
SvgNotData,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 当前选中菜单
|
||||
tabIndex: 0,
|
||||
menus: [
|
||||
{ name: '群信息' },
|
||||
{ name: '群成员' },
|
||||
{ name: '群公告' },
|
||||
{ name: '群设置' },
|
||||
],
|
||||
|
||||
loading: false,
|
||||
form: {
|
||||
group_name: '',
|
||||
profile: '',
|
||||
avatar: '',
|
||||
},
|
||||
rules: {
|
||||
group_name: [
|
||||
{
|
||||
required: true,
|
||||
message: '用户昵称不能为空!',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
detail: {
|
||||
group_name: '',
|
||||
profile: '',
|
||||
avatar: '',
|
||||
},
|
||||
|
||||
// 群成员列表
|
||||
searchMembers: '',
|
||||
batchDelMember: false,
|
||||
members: [],
|
||||
|
||||
// 群公告相关数据
|
||||
notice: {
|
||||
isShowform: false,
|
||||
loading: false,
|
||||
form: {
|
||||
id: 0,
|
||||
title: '',
|
||||
content: '',
|
||||
},
|
||||
rules: {
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
message: '标题不能为空!',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
content: [
|
||||
{
|
||||
required: true,
|
||||
message: '详情不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
items: [],
|
||||
},
|
||||
|
||||
inviteFriendBox: false,
|
||||
isAvatarCropper: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filterMembers() {
|
||||
return this.searchMembers == ''
|
||||
? this.members
|
||||
: this.members.filter(item => {
|
||||
return (
|
||||
item.nickname.match(this.searchMembers) != null ||
|
||||
item.visit_card.match(this.searchMembers) != null
|
||||
)
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.loadGroupDetail()
|
||||
this.loadMembers()
|
||||
this.loadNotices()
|
||||
},
|
||||
methods: {
|
||||
// 加载群信息
|
||||
loadGroupDetail() {
|
||||
ServeGroupDetail({
|
||||
group_id: this.groupId,
|
||||
}).then(({ code, data }) => {
|
||||
if (code == 200) {
|
||||
this.form = this.detail = {
|
||||
group_name: data.group_name,
|
||||
profile: data.profile,
|
||||
avatar: data.avatar,
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 加载群组成员列表
|
||||
loadMembers() {
|
||||
ServeGetGroupMembers({
|
||||
group_id: this.groupId,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.members = res.data.map(item => {
|
||||
item.is_delete = false
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 加载群组公告列表
|
||||
loadNotices() {
|
||||
ServeGetGroupNotices({
|
||||
group_id: this.groupId,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.notice.items = res.data.map(item => {
|
||||
item.isShow = false
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 修改群信息
|
||||
editGroup() {
|
||||
this.$refs.groupForm.validate(valid => {
|
||||
if (!valid) return false
|
||||
this.loading = true
|
||||
ServeEditGroup({
|
||||
group_id: this.groupId,
|
||||
group_name: this.form.group_name,
|
||||
profile: this.form.profile,
|
||||
avatar: this.form.avatar,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.detail.group_name = this.form.group_name
|
||||
this.detail.profile = this.form.profile
|
||||
this.detail.avatar = this.form.avatar
|
||||
|
||||
this.$message({
|
||||
message: '信息修改成功...',
|
||||
type: 'success',
|
||||
})
|
||||
} else {
|
||||
this.$message('信息修改失败...')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 左侧菜单栏切换事件
|
||||
triggerTab(i) {
|
||||
this.tabIndex = i
|
||||
},
|
||||
|
||||
// 关闭头像裁剪弹出层
|
||||
closeAvatarCropper(type, avatar = '') {
|
||||
this.isAvatarCropper = false
|
||||
if (type == 1 && avatar != '') {
|
||||
this.form.avatar = avatar
|
||||
}
|
||||
},
|
||||
|
||||
// 显示编辑公告窗口
|
||||
showNoticeBox(id = 0, title = '', content = '') {
|
||||
this.notice.isShowform = true
|
||||
this.notice.form.id = id
|
||||
this.notice.form.title = title
|
||||
this.notice.form.content = content
|
||||
},
|
||||
|
||||
// 编辑公告提交事件
|
||||
onSubmitNotice() {
|
||||
this.$refs.noticeForm.validate(valid => {
|
||||
if (!valid) return false
|
||||
|
||||
this.notice.loading = true
|
||||
ServeEditGroupNotice({
|
||||
notice_id: this.notice.form.id,
|
||||
group_id: this.groupId,
|
||||
title: this.notice.form.title,
|
||||
content: this.notice.form.content,
|
||||
is_top: 0,
|
||||
is_confirm: 0,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.notice.isShowform = false
|
||||
this.loadNotices()
|
||||
this.$notify({
|
||||
title: '消息提示',
|
||||
message: this.notice.form.id
|
||||
? '群公告修改成功...'
|
||||
: '群公告添加成功...',
|
||||
type: 'success',
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: '消息提示',
|
||||
message: this.notice.form.id
|
||||
? '群公告修改失败...'
|
||||
: '群公告添加失败...',
|
||||
type: 'success',
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.$notify({
|
||||
title: '消息提示',
|
||||
message: '网络异常,请稍后再试...',
|
||||
position: 'bottom-right',
|
||||
type: 'warning',
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.notice.loading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 展开/收起群公告详情
|
||||
catNoticeDetail(index) {
|
||||
this.notice.items[index].isShow = !this.notice.items[index].isShow
|
||||
},
|
||||
|
||||
// 查看群成员信息事件
|
||||
catUserDetail(item) {
|
||||
this.$refs.userBusinessCard.open(item.user_id)
|
||||
},
|
||||
|
||||
// 选中删除成员事件
|
||||
triggerDelBtn(member) {
|
||||
let i = this.members.findIndex(item => {
|
||||
return item.id === member.id
|
||||
})
|
||||
|
||||
this.members[i].is_delete = !this.members[i].is_delete
|
||||
},
|
||||
|
||||
// 批量删除群成员
|
||||
deleteMembers() {
|
||||
let ids = [],
|
||||
names = []
|
||||
|
||||
this.members.forEach(item => {
|
||||
if (item.is_delete) {
|
||||
ids.push(item.user_id)
|
||||
names.push(item.nickname)
|
||||
}
|
||||
})
|
||||
|
||||
if (ids.length == 0) {
|
||||
this.batchDelMember = false
|
||||
return
|
||||
}
|
||||
|
||||
this.$confirm(`您确定要将【 ${names.join('、')}】移出群聊?`, '温馨提示', {
|
||||
confirmButtonText: '确定删除',
|
||||
cancelButtonText: '取消',
|
||||
dangerouslyUseHTMLString: true,
|
||||
customClass: 'border-radius0',
|
||||
})
|
||||
.then(() => {
|
||||
ServeRemoveMembersGroup({
|
||||
group_id: this.groupId,
|
||||
members_ids: ids.join(','),
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.loadMembers()
|
||||
this.$notify({
|
||||
title: '删除成功',
|
||||
message: `已成功将群成员移除群组...`,
|
||||
type: 'success',
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.members.map(item => {
|
||||
return (item.is_delete = false)
|
||||
})
|
||||
this.batchDelMember = false
|
||||
})
|
||||
},
|
||||
|
||||
// 好友邀请成功回调方法
|
||||
inviteSuccess() {
|
||||
this.inviteFriendBox = false
|
||||
this.loadMembers()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 80%;
|
||||
height: 500px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.aside-border {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 8px;
|
||||
border-right: 1px solid #f5f5f5;
|
||||
|
||||
.menu-list {
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
margin: 8px 2px;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
border-left: 3px solid white;
|
||||
padding-left: 10px;
|
||||
|
||||
&.selectd {
|
||||
color: #2196f3;
|
||||
border-color: #2196f3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-col {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar-box {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
box-shadow: 0px 0px 7px 1px #e8e4e4;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: ease 0.5s;
|
||||
|
||||
.upload-icon {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(184, 184, 197, 0.2);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
i {
|
||||
font-size: 30px;
|
||||
color: #1bb0f3;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.upload-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
z-index: 3;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
i {
|
||||
font-size: 30px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .upload-mask {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
/* 群成员相关 start */
|
||||
.members {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
padding: 5px 20px;
|
||||
justify-content: space-between;
|
||||
|
||||
.member {
|
||||
width: 48%;
|
||||
height: 70px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
border: 1px dashed #e2dcdc;
|
||||
margin: 5px 0;
|
||||
padding: 3px;
|
||||
transition: ease 0.5s;
|
||||
|
||||
.larkc-tag {
|
||||
color: #3370ff;
|
||||
background-color: #e1eaff;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.avatar {
|
||||
flex: 1 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 3px 5px;
|
||||
|
||||
.nickname {
|
||||
font-size: 13px;
|
||||
margin: 0 5px 0 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.tools {
|
||||
flex-basis: 50px;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
padding-right: 5px;
|
||||
|
||||
i {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.is-delete {
|
||||
color: #03a9f4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.profile {
|
||||
color: #8f8a8a;
|
||||
font-size: 12px;
|
||||
padding: 3px 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.selectd {
|
||||
border-color: #2196f3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 群成员相关 end */
|
||||
|
||||
/* 公告相关 start */
|
||||
.notice-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.empty-notice {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
span {
|
||||
color: #cccccc;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.notices {
|
||||
.notice {
|
||||
cursor: pointer;
|
||||
min-height: 76px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px dashed #e2dcdc;
|
||||
margin-bottom: 15px;
|
||||
margin-right: 15px;
|
||||
padding-bottom: 5px;
|
||||
margin: 2px 20px 15px 15px;
|
||||
|
||||
h6 {
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 30px;
|
||||
|
||||
.left-title {
|
||||
flex: 1 1;
|
||||
height: 100%;
|
||||
line-height: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.right-tools {
|
||||
flex-basis: 70px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
line-height: 30px;
|
||||
text-align: right;
|
||||
font-weight: 300;
|
||||
font-size: 12px;
|
||||
color: #2196f3;
|
||||
}
|
||||
}
|
||||
|
||||
.datetime {
|
||||
font-size: 10px;
|
||||
color: #a59696;
|
||||
font-weight: 300;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin: 10px 0;
|
||||
|
||||
.text {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
color: #2196f3;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.retract {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 12px;
|
||||
line-height: 28px;
|
||||
font-weight: 500;
|
||||
color: #7d7a7a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notice-box {
|
||||
position: relative;
|
||||
padding: 28px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.2);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
height: 415px;
|
||||
width: 420px;
|
||||
|
||||
h4 {
|
||||
margin-bottom: 20px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
/* 公告相关 end */
|
||||
|
||||
/deep/.el-scrollbar__wrap {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -1,231 +0,0 @@
|
||||
<template>
|
||||
<div class="lum-dialog-mask">
|
||||
<el-container class="lum-dialog-box">
|
||||
<el-header class="header no-select" height="60px">
|
||||
<p>群公告({{ items.length }})</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="$emit('close')" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main no-padding">
|
||||
<template v-if="loadStatus == 0">
|
||||
<Loading text="正在努力加载中 ..." />
|
||||
</template>
|
||||
<template v-else-if="loadStatus == 1 && items.length == 0">
|
||||
<div class="loading">
|
||||
<SvgNotData class="svg-icon" />
|
||||
<p>暂无群公告</p>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="loadStatus == 2">
|
||||
<div class="loading">
|
||||
<i
|
||||
class="el-icon-warning"
|
||||
style="font-size: 50px; color: #ff5151"
|
||||
/>
|
||||
<p>
|
||||
加载失败,
|
||||
<a @click="loadNotices" class="pointer">点击重试</a>...
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-scrollbar class="full-height" :native="false" tag="section">
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="item.id"
|
||||
class="notice-item"
|
||||
>
|
||||
<div class="title">
|
||||
<span class="left-title">{{ item.title }}</span>
|
||||
<span
|
||||
class="right-tools no-select"
|
||||
@click="catNoticeDetail(index)"
|
||||
>
|
||||
{{ item.isShow ? '收起' : '展开' }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="datetime">
|
||||
<el-avatar :size="15" :src="item.avatar">
|
||||
<img src="~@/assets/image/detault-avatar.jpg" />
|
||||
</el-avatar>
|
||||
<span
|
||||
class="text nickname"
|
||||
@click="$refs.userBusinessCard.open(item.user_id)"
|
||||
>
|
||||
{{ item.nickname }}
|
||||
</span>
|
||||
<span class="text">发表于 {{ item.created_at }}</span>
|
||||
</p>
|
||||
<p class="content" :class="{ retract: !item.isShow }">
|
||||
{{ item.content }}
|
||||
</p>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ServeGetGroupNotices } from '@/api/group'
|
||||
import { SvgNotData } from '@/core/icons'
|
||||
import Loading from '@/components/global/Loading'
|
||||
export default {
|
||||
name: 'GroupNotice',
|
||||
props: {
|
||||
groupId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
SvgNotData,
|
||||
Loading,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 公告列表
|
||||
items: [],
|
||||
loadStatus: 0,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadNotices()
|
||||
},
|
||||
methods: {
|
||||
// 加载群组公告列表
|
||||
loadNotices() {
|
||||
this.loadStatus = 0
|
||||
ServeGetGroupNotices({
|
||||
group_id: this.groupId,
|
||||
})
|
||||
.then(({ code, data }) => {
|
||||
if (code == 200) {
|
||||
this.items = data.map(item => {
|
||||
item.isShow = false
|
||||
return item
|
||||
})
|
||||
|
||||
this.loadStatus = 1
|
||||
} else {
|
||||
this.loadStatus = 2
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loadStatus = 2
|
||||
})
|
||||
},
|
||||
// 展开/收起群公告详情
|
||||
catNoticeDetail(index) {
|
||||
this.items[index].isShow = !this.items[index].isShow
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 650px;
|
||||
max-width: 650px;
|
||||
height: 550px;
|
||||
|
||||
.main {
|
||||
overflow: hidden;
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 70%;
|
||||
font-size: 12px;
|
||||
|
||||
p {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
width: 80px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-item {
|
||||
cursor: pointer;
|
||||
min-height: 76px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px dashed #e2dcdc;
|
||||
padding-bottom: 5px;
|
||||
margin: 2px 20px 15px 15px;
|
||||
|
||||
h6 {
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 30px;
|
||||
|
||||
.left-title {
|
||||
flex: 1 1;
|
||||
height: 100%;
|
||||
line-height: 30px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.right-tools {
|
||||
flex-basis: 70px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
line-height: 30px;
|
||||
text-align: right;
|
||||
font-weight: 300;
|
||||
font-size: 12px;
|
||||
color: #2196f3;
|
||||
}
|
||||
}
|
||||
|
||||
.datetime {
|
||||
font-size: 10px;
|
||||
color: #a59696;
|
||||
font-weight: 300;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin: 10px 0;
|
||||
|
||||
.text {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
color: #2196f3;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.retract {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 13px;
|
||||
line-height: 28px;
|
||||
font-weight: 400;
|
||||
color: #706a6a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/deep/.el-scrollbar__wrap {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -1,817 +0,0 @@
|
||||
<template>
|
||||
<el-container class="container">
|
||||
<el-header class="header">
|
||||
<span>群信息</span>
|
||||
<el-tooltip content="发送消息" placement="top">
|
||||
<i class="icon-send el-icon-chat-line-square" @click="sendGroup" />
|
||||
</el-tooltip>
|
||||
<i class="el-icon-close" @click="$emit('close')" />
|
||||
</el-header>
|
||||
|
||||
<el-main class="main lum-scrollbar">
|
||||
<div class="list-item flex">
|
||||
<p>
|
||||
<span>群名称:</span>
|
||||
<span class="group-setting-title">{{ detail.groupName }}</span>
|
||||
</p>
|
||||
<span
|
||||
v-show="detail.is_manager"
|
||||
class="more"
|
||||
@click="isShowManager = true"
|
||||
>管理
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<span>群主:</span>
|
||||
<span class="group-boss-name">{{ detail.groupOwner }}</span>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<span>我的群昵称:</span>
|
||||
<span v-if="!isEditRemark" class="edit-visit-card">
|
||||
<span>
|
||||
{{ detail.visitCard }}
|
||||
<span v-show="!detail.visitCard" style="font-size: 12px">
|
||||
添加群名片
|
||||
</span>
|
||||
</span>
|
||||
<i
|
||||
class="el-icon-edit-outline edit-remark-icon"
|
||||
@click="
|
||||
isEditRemark = true
|
||||
editRemarkText = detail.visitCard
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
<span v-else>
|
||||
<input
|
||||
v-model="editRemarkText"
|
||||
class="edit-input"
|
||||
type="text"
|
||||
v-focus
|
||||
@keyup.enter="editRemark"
|
||||
/>
|
||||
<span class="input-submit" @click="editRemark">确认</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="list-item flex">
|
||||
<span>消息免打扰:</span>
|
||||
<el-switch
|
||||
v-model="detail.disturb"
|
||||
inactive-color="#e0d6d6"
|
||||
:disabled="disturbDisabled"
|
||||
@change="editDisturb"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 预留 -->
|
||||
<div class="list-item flex">
|
||||
<span>全员禁言:</span>
|
||||
<el-switch v-model="detail.no_message" inactive-color="#e0d6d6" />
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<span>群成员:</span>
|
||||
<span>{{ members.length }} 人</span>
|
||||
</div>
|
||||
|
||||
<div class="list-item-tips">群主已开启“新成员入群可查看所有聊天记录”</div>
|
||||
|
||||
<div class="list-item">群简介</div>
|
||||
|
||||
<div class="list-item-tips">
|
||||
{{ detail.groupProfile ? detail.groupProfile : '暂无群简介' }}
|
||||
</div>
|
||||
|
||||
<div class="list-item flex">
|
||||
<span>群公告</span>
|
||||
<span
|
||||
v-show="detail.group_notice"
|
||||
class="more"
|
||||
@click="isShowGroupNotice = true"
|
||||
>更多
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="list-item-tips group-notice">
|
||||
<span v-if="detail.group_notice.title">
|
||||
<b>#{{ detail.group_notice.title }}#</b><br />
|
||||
{{ detail.group_notice.content }}
|
||||
</span>
|
||||
<span v-else>暂无群公告</span>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<p class="group-invite" @click="addGroupMembers">
|
||||
<i class="el-icon-plus" />
|
||||
<span> 邀请好友</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<div class="member-box">
|
||||
<div class="view-box">
|
||||
<i class="iconfont icon-sousuo i-sousuo" />
|
||||
<input type="text" placeholder="搜索群成员" v-model="keywords" />
|
||||
</div>
|
||||
|
||||
<el-row class="row-header">
|
||||
<el-col :span="11">昵称</el-col>
|
||||
<el-col :span="8">名片</el-col>
|
||||
<el-col :span="5">性别</el-col>
|
||||
</el-row>
|
||||
|
||||
<template v-if="searchs.length == 0">
|
||||
<el-row class="row-items">
|
||||
<el-col :span="24">
|
||||
<p style="text-align:center;">无数据</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-row
|
||||
v-for="member in searchs"
|
||||
:key="member.user_id"
|
||||
class="row-items"
|
||||
@click.native="openUserDetail(member.user_id)"
|
||||
>
|
||||
<el-col :span="11">
|
||||
<img
|
||||
width="20px"
|
||||
:src="member.avatar"
|
||||
:onerror="$store.state.detaultAvatar"
|
||||
/>
|
||||
<span class="nickname">{{ member.nickname }}</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span>{{ member.visit_card ? member.visit_card : '-' }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span v-if="member.gender == 1">男</span>
|
||||
<span v-else-if="member.gender == 2">女</span>
|
||||
<span v-else>未知</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
<el-footer class="footer">
|
||||
<button @click="isShowSignout = true">退出该群聊</button>
|
||||
</el-footer>
|
||||
|
||||
<!-- 退群提示层 -->
|
||||
<div class="signout-box no-select" v-show="isShowSignout">
|
||||
<p v-show="signoutStatus == 0">您确认退出当前群聊吗?</p>
|
||||
<p v-show="signoutStatus == 0">退群后群聊信息将不能查看</p>
|
||||
<p v-show="signoutStatus == 0" class="signout-btn">
|
||||
<button @click="signout">确认</button>
|
||||
<button @click="isShowSignout = false">取消</button>
|
||||
</p>
|
||||
|
||||
<p v-show="signoutStatus == 1" class="signout-btn mt38">
|
||||
<span style="color: #ccc">
|
||||
<i class="el-icon-loading" />
|
||||
正在退出群聊...
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p v-show="signoutStatus == 2" class="signout-btn mt38">
|
||||
<span style="color: #cccccc">退出群聊失败,请3(s)后再试...</span>
|
||||
</p>
|
||||
|
||||
<p v-show="signoutStatus == 3" class="signout-btn mt38">
|
||||
<span style="color: #339e19">
|
||||
<i class="iconfont icon-success_no_circle" /> 已成功退出群聊...
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 查看好友用户信息 -->
|
||||
<UserBusinessCard ref="userBusinessCard" />
|
||||
|
||||
<!-- 邀请好友组件 -->
|
||||
<transition name="el-fade-in-linear">
|
||||
<GroupLaunch
|
||||
v-if="inviteFriendBox"
|
||||
:group-id="groupId"
|
||||
@close="inviteFriendBox = false"
|
||||
@invite-success="inviteSuccess"
|
||||
/>
|
||||
</transition>
|
||||
|
||||
<!-- 群管理组件 -->
|
||||
<transition name="el-fade-in-linear">
|
||||
<GroupManage
|
||||
v-if="isShowManager"
|
||||
:group-id="groupId"
|
||||
@close="isShowManager = false"
|
||||
/>
|
||||
</transition>
|
||||
|
||||
<!-- 群公告组件 -->
|
||||
<transition name="el-fade-in-linear">
|
||||
<GroupNotice
|
||||
v-if="isShowGroupNotice"
|
||||
:group-id="groupId"
|
||||
@close="isShowGroupNotice = false"
|
||||
/>
|
||||
</transition>
|
||||
</el-container>
|
||||
</template>
|
||||
<script>
|
||||
import { ServeSetNotDisturb } from '@/api/chat'
|
||||
import {
|
||||
ServeGroupDetail,
|
||||
ServeUpdateGroupCard,
|
||||
ServeSecedeGroup,
|
||||
ServeGetGroupMembers,
|
||||
} from '@/api/group'
|
||||
|
||||
//创建群聊组件
|
||||
import GroupLaunch from '@/components/group/GroupLaunch'
|
||||
import UserBusinessCard from '@/components/user/UserBusinessCard'
|
||||
import GroupManage from '@/components/group/GroupManage'
|
||||
import GroupNotice from '@/components/group/GroupNotice'
|
||||
|
||||
export default {
|
||||
name: 'GroupPanel',
|
||||
components: {
|
||||
GroupLaunch,
|
||||
UserBusinessCard,
|
||||
GroupManage,
|
||||
GroupNotice,
|
||||
},
|
||||
props: {
|
||||
groupId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
detail: {
|
||||
groupAvatar: '',
|
||||
groupId: 0,
|
||||
groupName: '',
|
||||
groupOwner: '',
|
||||
groupProfile: '',
|
||||
disturb: 0,
|
||||
no_message: false,
|
||||
visitCard: '',
|
||||
is_manager: false,
|
||||
group_notice: [],
|
||||
},
|
||||
|
||||
keywords: '',
|
||||
members: [],
|
||||
|
||||
isEditRemark: false,
|
||||
editRemarkText: '',
|
||||
|
||||
inviteFriendBox: false,
|
||||
isShowSignout: false,
|
||||
|
||||
signoutStatus: 0,
|
||||
|
||||
disturbDisabled: false,
|
||||
|
||||
// 是否显示群管理窗口
|
||||
isShowManager: false,
|
||||
|
||||
// 是否显示群公告窗口
|
||||
isShowGroupNotice: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
groupId: function(value) {
|
||||
if (value > 0) {
|
||||
this.loadGroupDetail()
|
||||
this.loadMembers()
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
searchs() {
|
||||
return this.keywords == ''
|
||||
? this.members
|
||||
: this.members.filter(item => {
|
||||
return (
|
||||
item.nickname.match(this.keywords) != null ||
|
||||
item.user_card.match(this.keywords) != null
|
||||
)
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (parseInt(this.groupId) > 0) {
|
||||
this.loadGroupDetail()
|
||||
this.loadMembers()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 加载群组成员列表
|
||||
loadMembers() {
|
||||
ServeGetGroupMembers({
|
||||
group_id: this.groupId,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.members = res.data
|
||||
this.$emit('group-info', {
|
||||
group_id: this.members.groupId,
|
||||
members_num: this.members.length,
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 加载群信息
|
||||
loadGroupDetail() {
|
||||
this.isEditRemark = false
|
||||
ServeGroupDetail({
|
||||
group_id: this.groupId,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
let result = res.data
|
||||
this.detail.groupAvatar = result.avatar
|
||||
this.detail.groupId = result.group_id
|
||||
this.detail.userId = res.data.user_id
|
||||
this.detail.groupName = result.group_name
|
||||
this.detail.groupOwner = result.manager_nickname
|
||||
this.detail.groupProfile = result.group_profile
|
||||
this.detail.disturb = result.not_disturb == 1 ? true : false
|
||||
this.detail.visitCard = result.visit_card
|
||||
this.detail.is_manager = result.is_manager
|
||||
|
||||
if (result.notice) {
|
||||
this.detail.group_notice = result.notice
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 设置群免打扰状态
|
||||
editDisturb(value) {
|
||||
this.disturbDisabled = true
|
||||
ServeSetNotDisturb({
|
||||
type: 2,
|
||||
receive_id: this.groupId,
|
||||
not_disturb: value ? 1 : 0,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.$emit('disturb-change', {
|
||||
group_id: this.groupId,
|
||||
status: value ? 1 : 0,
|
||||
})
|
||||
} else {
|
||||
this.detail.disturb = value ? 0 : 1
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.detail.disturb = value ? 0 : 1
|
||||
})
|
||||
.finally(() => {
|
||||
this.disturbDisabled = false
|
||||
})
|
||||
},
|
||||
|
||||
// 设置用户群名片
|
||||
editRemark() {
|
||||
if (this.editRemarkText == '') {
|
||||
this.isEditRemark = false
|
||||
return
|
||||
}
|
||||
|
||||
if (this.detail.visitCard == this.editRemarkText) {
|
||||
this.isEditRemark = false
|
||||
return
|
||||
}
|
||||
|
||||
ServeUpdateGroupCard({
|
||||
group_id: this.groupId,
|
||||
visit_card: this.editRemarkText,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.isEditRemark = false
|
||||
this.detail.visitCard = this.editRemarkText
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 查看用户信息
|
||||
openUserDetail(user_id) {
|
||||
this.$refs.userBusinessCard.open(user_id)
|
||||
},
|
||||
|
||||
// 邀请好友加入群聊
|
||||
addGroupMembers() {
|
||||
sessionStorage.setItem('invite_group_id', this.detail.groupId)
|
||||
this.inviteFriendBox = true
|
||||
},
|
||||
|
||||
// 邀请好友成功之后的回调事件
|
||||
inviteSuccess() {
|
||||
this.inviteFriendBox = false
|
||||
this.loadMembers()
|
||||
|
||||
this.$notify({
|
||||
title: '邀请成功',
|
||||
message: `好友已成功加入群组...`,
|
||||
type: 'success',
|
||||
})
|
||||
},
|
||||
|
||||
// 发送群聊
|
||||
sendGroup() {
|
||||
this.$emit('send-group', this.detail.groupId)
|
||||
},
|
||||
|
||||
// 退出群操操作
|
||||
signout() {
|
||||
this.signoutStatus = 1
|
||||
ServeSecedeGroup({
|
||||
group_id: this.detail.groupId,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.signoutStatus = 3
|
||||
setTimeout(() => {
|
||||
this.signoutStatus = 0
|
||||
this.isShowSignout = false
|
||||
this.$emit('quit-group')
|
||||
}, 1500)
|
||||
} else {
|
||||
this.signoutStatus = 2
|
||||
setTimeout(() => {
|
||||
this.signoutStatus = 0
|
||||
}, 3000)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.signoutStatus = 2
|
||||
setTimeout(() => {
|
||||
this.signoutStatus = 0
|
||||
}, 3000)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
height: 100%;
|
||||
|
||||
.header {
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
box-shadow: -1px 0px 5px 0px #cccccc45;
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.icon-send {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: 22px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.el-icon-close {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 22px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.list-item {
|
||||
position: relative;
|
||||
padding: 16px 16px 0;
|
||||
min-height: 18px;
|
||||
font-size: 14px;
|
||||
|
||||
&.flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.edit-visit-card {
|
||||
position: initial;
|
||||
color: #a29f9f;
|
||||
}
|
||||
|
||||
.edit-remark-icon {
|
||||
margin-left: 5px;
|
||||
color: rgb(169, 184, 187);
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.edit-input {
|
||||
width: 46%;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
border: 1px solid #92cbff;
|
||||
padding-left: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.input-submit {
|
||||
width: 55px;
|
||||
text-align: center;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
background-color: #008cee;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
color: #fff !important;
|
||||
font-size: 12px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.group-setting-title {
|
||||
max-width: 250px;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.group-boss-name {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
max-width: 180px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.group-invite {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
color: #877272;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
border: 1px dashed #d9bbbb;
|
||||
font-size: 13px;
|
||||
transition: all 0.5s ease-in-out;
|
||||
&:hover {
|
||||
color: #ff5722;
|
||||
border-color: #ff5722;
|
||||
transform: scale(1.01);
|
||||
}
|
||||
}
|
||||
|
||||
.more {
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.list-item-tips {
|
||||
font-size: 12px;
|
||||
color: #b1b1b1;
|
||||
margin-top: 5px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
font-weight: 300;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.group-notice {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.member-box {
|
||||
min-height: 180px;
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #ecebeb;
|
||||
border-radius: 3px;
|
||||
|
||||
.view-box {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 15px;
|
||||
position: relative;
|
||||
|
||||
input {
|
||||
width: calc(100% - 40px);
|
||||
height: 30px;
|
||||
line-height: 28px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #f1e9e9;
|
||||
color: #b3b0b0;
|
||||
font-size: 13px;
|
||||
padding: 0 10px 0 30px;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: #ccc9c9;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.i-sousuo {
|
||||
color: rgb(179, 176, 176);
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 9px;
|
||||
}
|
||||
|
||||
span {
|
||||
position: relative;
|
||||
|
||||
i {
|
||||
font-size: 24px;
|
||||
top: -3px;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row-header {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid #e0dddd;
|
||||
|
||||
div {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
|
||||
&:nth-child(2) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row-items {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
margin-bottom: 3px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
div {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
|
||||
&:nth-child(2) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
margin-left: 5px;
|
||||
&:hover {
|
||||
color: #3685d6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.signout-box {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background: #ffffff;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
bottom: 0;
|
||||
box-shadow: -1px -3px 11px 0px #cccccc82;
|
||||
-webkit-animation: showFooter 0.35s ease-in-out;
|
||||
-moz-animation: showFooter 0.35s ease-in-out;
|
||||
animation: showFooter 0.35s ease-in-out;
|
||||
|
||||
p {
|
||||
&:first-child {
|
||||
text-align: center;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #cccccc;
|
||||
}
|
||||
}
|
||||
|
||||
.mt38 {
|
||||
margin-top: 38px;
|
||||
}
|
||||
}
|
||||
|
||||
.signout-btn {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
|
||||
button {
|
||||
height: 30px;
|
||||
width: 90px;
|
||||
line-height: 30px;
|
||||
background: #007fbb;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
|
||||
&:first-child {
|
||||
background: #ff3333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
background: #f1eded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container .footer {
|
||||
height: 60px;
|
||||
padding: 0;
|
||||
line-height: 60px;
|
||||
text-align: center;
|
||||
background-color: #f8f8f8;
|
||||
|
||||
button {
|
||||
width: 180px;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
background: #ed3c3b;
|
||||
border-radius: 3px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
margin: auto auto;
|
||||
|
||||
&:active {
|
||||
background: #f5b8b8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes showFooter {
|
||||
0% {
|
||||
transform: translateY(75px);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes showFooter {
|
||||
0% {
|
||||
-webkit-transform: translateY(75px);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,80 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="abs-module" v-show="isShow">
|
||||
<div class="abs-box">
|
||||
<i class="el-icon-circle-close" @click="close" />
|
||||
<a href="https://www.aliyun.com/1111/new?userCode=kqyyppx2">
|
||||
<img src="~@/assets/image/aliyun-abs.jpg" width="300" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.getNum() <= 2) {
|
||||
setTimeout(() => {
|
||||
this.isShow = true
|
||||
}, 1000 * 60 * 2)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNum() {
|
||||
return parseInt(sessionStorage.getItem('ABS_BOX')) || 0
|
||||
},
|
||||
close() {
|
||||
sessionStorage.setItem('ABS_BOX', this.getNum() + 1)
|
||||
this.isShow = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.abs-module {
|
||||
position: fixed;
|
||||
width: 300px;
|
||||
height: 163.63px;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
border-radius: 5px;
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
transition: all 2s;
|
||||
animation: absfade 1000ms infinite;
|
||||
|
||||
.abs-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes absfade {
|
||||
from {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,246 +0,0 @@
|
||||
<template>
|
||||
<div class="lum-dialog-mask">
|
||||
<el-container class="lum-dialog-box">
|
||||
<el-header class="header" height="50px">
|
||||
<p>选择头像</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="$emit('close', 0)" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main">
|
||||
<el-container class="full-height">
|
||||
<el-aside width="400px">
|
||||
<div class="cropper-box">
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
mode="cover"
|
||||
:img="option.img"
|
||||
:output-size="option.size"
|
||||
:output-type="option.outputType"
|
||||
:info="true"
|
||||
:full="option.full"
|
||||
:fixed="fixed"
|
||||
:fixed-number="fixedNumber"
|
||||
:can-move="option.canMove"
|
||||
:can-move-box="option.canMoveBox"
|
||||
:fixed-box="option.fixedBox"
|
||||
:original="option.original"
|
||||
:auto-crop="option.autoCrop"
|
||||
:auto-crop-width="option.autoCropWidth"
|
||||
:auto-crop-height="option.autoCropHeight"
|
||||
:center-box="option.centerBox"
|
||||
:high="option.high"
|
||||
@real-time="realTime"
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
id="uploads"
|
||||
ref="fileInput"
|
||||
accept="image/png, image/jpeg, image/jpg"
|
||||
style="display: none"
|
||||
@change="uploadImg($event, 1)"
|
||||
/>
|
||||
</div>
|
||||
<div class="tools tools-flex">
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
icon="el-icon-upload"
|
||||
@click="clickUpload"
|
||||
>上传图片
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
icon="el-icon-refresh"
|
||||
@click="refreshCrop"
|
||||
>刷新
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
icon="el-icon-refresh-left"
|
||||
@click="rotateLeft"
|
||||
>左转
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
icon="el-icon-refresh-right"
|
||||
@click="rotateRight"
|
||||
>右转
|
||||
</el-button>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main class="no-padding">
|
||||
<div class="cropper-box">
|
||||
<div class="preview-img">
|
||||
<img v-if="cusPreviewsImg" :src="cusPreviewsImg" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="tools">
|
||||
<el-button type="primary" size="small" @click="uploadService">
|
||||
保存图片
|
||||
</el-button>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { VueCropper } from 'vue-cropper'
|
||||
import { ServeUploadFileStream } from '@/api/upload'
|
||||
|
||||
export default {
|
||||
name: 'AvatarCropper',
|
||||
components: {
|
||||
VueCropper,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cusPreviewsImg: '',
|
||||
previews: {},
|
||||
option: {
|
||||
img: '',
|
||||
size: 1,
|
||||
full: false,
|
||||
outputType: 'png',
|
||||
canMove: true,
|
||||
fixedBox: true,
|
||||
original: false,
|
||||
canMoveBox: true,
|
||||
autoCrop: true,
|
||||
// 只有自动截图开启 宽度高度才生效
|
||||
autoCropWidth: 200,
|
||||
autoCropHeight: 150,
|
||||
centerBox: false,
|
||||
high: true,
|
||||
},
|
||||
fixed: true,
|
||||
fixedNumber: [1, 1],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickUpload() {
|
||||
this.$refs.fileInput.click()
|
||||
},
|
||||
clearCrop() {
|
||||
if (!this.cusPreviewsImg) return false
|
||||
this.$refs.cropper.clearCrop()
|
||||
},
|
||||
refreshCrop() {
|
||||
if (!this.cusPreviewsImg) return false
|
||||
this.$refs.cropper.refresh()
|
||||
},
|
||||
rotateLeft() {
|
||||
if (!this.cusPreviewsImg) return false
|
||||
this.$refs.cropper.rotateLeft()
|
||||
},
|
||||
rotateRight() {
|
||||
if (!this.cusPreviewsImg) return false
|
||||
this.$refs.cropper.rotateRight()
|
||||
},
|
||||
// 实时预览函数
|
||||
realTime() {
|
||||
this.$refs.cropper.getCropData(img => {
|
||||
this.cusPreviewsImg = img
|
||||
})
|
||||
},
|
||||
|
||||
// 上传回调事件
|
||||
uploadImg(e, num) {
|
||||
let file = e.target.files[0]
|
||||
if (!/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG)$/.test(e.target.value)) {
|
||||
this.$message('图片类型必须是.gif,jpeg,jpg,png,bmp中的一种')
|
||||
return false
|
||||
}
|
||||
|
||||
let reader = new FileReader()
|
||||
reader.onload = e => {
|
||||
let data
|
||||
if (typeof e.target.result === 'object') {
|
||||
// 把Array Buffer转化为blob 如果是base64不需要
|
||||
data = window.URL.createObjectURL(new Blob([e.target.result]))
|
||||
} else {
|
||||
data = e.target.result
|
||||
}
|
||||
if (num === 1) {
|
||||
this.option.img = data
|
||||
} else if (num === 2) {
|
||||
this.example2.img = data
|
||||
}
|
||||
}
|
||||
// 转化为base64
|
||||
// reader.readAsDataURL(file)
|
||||
// 转化为blob
|
||||
reader.readAsArrayBuffer(file)
|
||||
},
|
||||
|
||||
// 上传图片到服务器
|
||||
uploadService() {
|
||||
if (this.cusPreviewsImg == '') return
|
||||
ServeUploadFileStream({
|
||||
fileStream: this.cusPreviewsImg,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.$emit('close', 1, res.data.avatar)
|
||||
} else {
|
||||
this.$message('文件上传失败,请稍后再试...')
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message('文件上传失败,请稍后再试...')
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
height: 550px;
|
||||
max-width: 800px;
|
||||
|
||||
.main {
|
||||
.cropper-box {
|
||||
height: 400px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.preview-img {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 4px #ccc;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tools {
|
||||
height: 40px;
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
|
||||
button {
|
||||
border-radius: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.tools-flex {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,129 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="reward" v-show="isShow">
|
||||
<div class="title">
|
||||
<span>Donate</span>
|
||||
<i class="el-icon-circle-close" @click="close" />
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="pay-box">
|
||||
<img
|
||||
src="https://cdn.learnku.com/uploads/images/202101/30/46424/PPYHOUhCb4.jpg"
|
||||
/>
|
||||
<p>支付宝</p>
|
||||
</div>
|
||||
<div class="pay-box">
|
||||
<img
|
||||
src="https://cdn.learnku.com/uploads/images/202101/30/46424/XLmCJjbvlQ.png"
|
||||
/>
|
||||
<p>微信</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
开源不易,如果你觉得项目对你有帮助,可以请作者喝杯咖啡☕️!鼓励下...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.getNum() <= 3) {
|
||||
setTimeout(() => {
|
||||
this.isShow = true
|
||||
}, 1000 * 30)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNum() {
|
||||
return parseInt(localStorage.getItem('REWARD_BOX')) || 0
|
||||
},
|
||||
close() {
|
||||
localStorage.setItem('REWARD_BOX', this.getNum() + 1)
|
||||
this.isShow = false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.reward {
|
||||
position: fixed;
|
||||
width: 550px;
|
||||
height: 400px;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 12px #ccc;
|
||||
border: 1px solid rgb(228, 225, 225);
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
z-index: 9999;
|
||||
background: white;
|
||||
|
||||
.title {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
padding-left: 20px;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
background: #f9f7f7;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 18px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
height: 300px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
.pay-box {
|
||||
width: 200px;
|
||||
height: 240px;
|
||||
background: #1977ff;
|
||||
margin: 0 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 5px;
|
||||
|
||||
img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 20px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
background: #22ab38;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 待开发
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
skins: [
|
||||
{
|
||||
theme: '',
|
||||
class: 'default',
|
||||
text: '默认',
|
||||
},
|
||||
{
|
||||
theme: '',
|
||||
class: 'red',
|
||||
text: '红色',
|
||||
},
|
||||
{
|
||||
theme: '',
|
||||
class: 'dark',
|
||||
text: '暗黑',
|
||||
},
|
||||
{
|
||||
theme: '',
|
||||
class: 'blue',
|
||||
text: '浅蓝',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
||||
@@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<div class="welcome-box">
|
||||
<div class="famous-box">
|
||||
<img src="~@/assets/image/chat.png" width="300" />
|
||||
<p v-if="text">
|
||||
{{ text }}
|
||||
</p>
|
||||
<p v-else>
|
||||
不是井里没有水,而是你挖的不够深<br />
|
||||
不是成功来得慢,而是你努力的不够多<br />
|
||||
加油吧! ......
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
components: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.welcome-box {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.famous-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
font-size: 24px;
|
||||
user-select: none;
|
||||
|
||||
p {
|
||||
width: 100%;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
color: #b9b4b4;
|
||||
margin-top: -30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,246 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>笔记附件列表({{ files.length }})</p>
|
||||
<div class="annex-box lum-scrollbar">
|
||||
<input ref="uploads" type="file" @change="uploadAnnex" />
|
||||
<div class="annex-main">
|
||||
<p v-show="files.length == 0" class="empty-text">
|
||||
暂无附件
|
||||
</p>
|
||||
|
||||
<div v-for="(file, i) in files" :key="file.id" class="file-item">
|
||||
<div class="suffix">{{ file.file_suffix }}</div>
|
||||
<div class="content">
|
||||
<div class="filename">{{ file.original_name }}</div>
|
||||
<div class="filetool">
|
||||
<span>{{ formateTime(file.created_at) }}</span>
|
||||
<span class="size">
|
||||
{{ formateSize(file.file_size) }}
|
||||
</span>
|
||||
<div class="tools">
|
||||
<i class="el-icon-download" @click="downloadAnnex(file.id)" />
|
||||
<i class="el-icon-delete" @click="deleteAnnex(file.id, i)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="annex-footer">
|
||||
<p class="notice-text">最多可支持上传{{ maxNum }}个附件</p>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-upload"
|
||||
:disabled="files.length >= maxNum"
|
||||
:loading="loadStatus"
|
||||
@click="$refs.uploads.click()"
|
||||
>{{ loadStatus ? '上传中...' : '上传附件' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
ServeDeleteArticleAnnex,
|
||||
ServeDownloadAnnex as downloadAnnex,
|
||||
ServeUploadArticleAnnex,
|
||||
} from '@/api/article'
|
||||
import { formateSize, formateTime, parseTime } from '@/utils/functions'
|
||||
|
||||
export default {
|
||||
name: 'NoteAnnexBox',
|
||||
props: {
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
files: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadStatus: false,
|
||||
disabled: false,
|
||||
maxNum: 10,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 格式化文件大小
|
||||
formateSize,
|
||||
|
||||
// 格式化时间显示格式
|
||||
formateTime,
|
||||
|
||||
// 下载笔记附件
|
||||
downloadAnnex,
|
||||
|
||||
// 删除笔记附件
|
||||
deleteAnnex(annex_id, index) {
|
||||
ServeDeleteArticleAnnex({
|
||||
annex_id,
|
||||
}).then(({ code }) => {
|
||||
if (code == 200) {
|
||||
this.$delete(this.files, index)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 上传笔记附件文件
|
||||
uploadAnnex(e) {
|
||||
if (e.target.files.length == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
let file = e.target.files[0]
|
||||
if (file.size / (1024 * 1024) > 5) {
|
||||
this.$message('笔记附件不能大于5M!')
|
||||
return false
|
||||
}
|
||||
|
||||
let fileData = new FormData()
|
||||
fileData.append('annex', file)
|
||||
fileData.append('article_id', this.id)
|
||||
|
||||
this.loadStatus = true
|
||||
ServeUploadArticleAnnex(fileData)
|
||||
.then(({ code, data }) => {
|
||||
if (code == 200) {
|
||||
this.files.push({
|
||||
id: data.id,
|
||||
original_name: data.original_name,
|
||||
created_at: parseTime(new Date()),
|
||||
file_size: data.file_size,
|
||||
file_suffix: data.file_suffix,
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadStatus = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/* 文件管理弹出层 */
|
||||
.annex-box {
|
||||
width: 300px;
|
||||
min-height: 50px;
|
||||
max-height: 675px;
|
||||
background-color: white;
|
||||
overflow-y: auto;
|
||||
|
||||
.annex-main {
|
||||
min-height: 30px;
|
||||
border-bottom: 1px solid rgb(239, 233, 233);
|
||||
margin-bottom: 8px;
|
||||
padding: 5px 0;
|
||||
|
||||
.empty-text {
|
||||
color: #969292;
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
height: 50px;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 10px;
|
||||
|
||||
.suffix {
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
background-color: #ffcc80;
|
||||
border-radius: 3px;
|
||||
float: left;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.content {
|
||||
float: left;
|
||||
width: 247px;
|
||||
height: 100%;
|
||||
|
||||
.filename {
|
||||
padding-left: 5px;
|
||||
color: #172b4d;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.filetool {
|
||||
color: #505f79;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
padding-left: 5px;
|
||||
margin-top: 9px;
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
margin: 0 3px;
|
||||
&.size {
|
||||
color: #3a8ee6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tools {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: 5px;
|
||||
width: 55px;
|
||||
height: 24px;
|
||||
text-align: right;
|
||||
line-height: 28px;
|
||||
|
||||
i {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.el-icon-download {
|
||||
color: #66b1ff;
|
||||
}
|
||||
|
||||
.el-icon-delete {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.annex-footer {
|
||||
.notice-text {
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
text-align: left;
|
||||
float: left;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,206 +0,0 @@
|
||||
<template>
|
||||
<div class="lum-dialog-mask">
|
||||
<el-container
|
||||
class="lum-dialog-box animated bounceInDown"
|
||||
v-outside="close"
|
||||
>
|
||||
<el-header height="60px" class="header no-select">
|
||||
<p>近 30 天删除的附件({{ tableData.length }})</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="close" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main lum-scrollbar">
|
||||
<el-table :data="tableData" size="mini">
|
||||
<div slot="empty">暂无相关数据</div>
|
||||
<el-table-column
|
||||
prop="original_name"
|
||||
label="附件名称"
|
||||
width="180"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" @click="downloadAnnex(scope.row.id)">{{
|
||||
scope.row.original_name
|
||||
}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="title"
|
||||
label="所属笔记"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="day"
|
||||
label="剩余天数"
|
||||
align="center"
|
||||
width="80"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
fixed="right"
|
||||
label="操作"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="recoverAnnex(scope.row)"
|
||||
>恢复</el-button
|
||||
>
|
||||
<el-popover
|
||||
placement="top"
|
||||
@hide="lock(false)"
|
||||
@show="lock(true)"
|
||||
:ref="`popover-${scope.$index}`"
|
||||
>
|
||||
<p style="margin-bottom: 10px">
|
||||
【{{
|
||||
scope.row.original_name
|
||||
}}】附件您确定要永久删除吗?<br />
|
||||
</p>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="
|
||||
scope._self.$refs[`popover-${scope.$index}`].doClose()
|
||||
"
|
||||
>
|
||||
取消</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
@click="deleteAnnex(scope.row, scope.$index)"
|
||||
>确定</el-button
|
||||
>
|
||||
</div>
|
||||
<el-button
|
||||
slot="reference"
|
||||
type="text"
|
||||
size="small"
|
||||
style="color: #ec5252; margin-left: 5px"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-main>
|
||||
<el-footer class="footer" height="30px">
|
||||
<p class="footer-tip">移动至回收站的附件和笔记,将在 30 天后自动清除</p>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import { Table, TableColumn } from 'element-ui'
|
||||
Vue.use(Table)
|
||||
Vue.use(TableColumn)
|
||||
|
||||
import {
|
||||
ServeGetRecoverAnnexList,
|
||||
ServeRecoverArticleAnnex,
|
||||
ServeDownloadAnnex,
|
||||
ServeForeverDeleteAnnex,
|
||||
} from '@/api/article'
|
||||
|
||||
export default {
|
||||
name: 'NoteAnnexRecycle',
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
closeLock: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadList()
|
||||
},
|
||||
methods: {
|
||||
loadList() {
|
||||
ServeGetRecoverAnnexList().then(res => {
|
||||
if (res.code == 200) {
|
||||
this.tableData = res.data.rows
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 关闭当前窗口
|
||||
close() {
|
||||
if (!this.closeLock) this.$emit('close')
|
||||
},
|
||||
|
||||
// 给遮罩层加锁
|
||||
lock(value) {
|
||||
this.closeLock = value
|
||||
},
|
||||
|
||||
// 恢复附件
|
||||
recoverAnnex(data, index) {
|
||||
ServeRecoverArticleAnnex({
|
||||
annex_id: data.id,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.tableData.splice(index, 1)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
//永久删除附件
|
||||
deleteAnnex(data, index) {
|
||||
ServeForeverDeleteAnnex({
|
||||
annex_id: data.id,
|
||||
})
|
||||
.then(res => {
|
||||
this.$refs[`popover-${index}`].doClose()
|
||||
this.lock(false)
|
||||
if (res.code == 200) {
|
||||
this.tableData.splice(index, 1)
|
||||
} else {
|
||||
this.$notify({
|
||||
message: '附件删除失败...',
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.$refs[`popover-${index}`].doClose()
|
||||
this.lock(false)
|
||||
})
|
||||
},
|
||||
|
||||
//下载笔记附件
|
||||
downloadAnnex: ServeDownloadAnnex,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 700px;
|
||||
height: 80%;
|
||||
max-width: 700px;
|
||||
|
||||
.main {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
.footer-tip {
|
||||
color: #a7afbc;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .tab-header-row .cell {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgb(172, 167, 167);
|
||||
}
|
||||
</style>
|
||||
@@ -1,220 +0,0 @@
|
||||
<template>
|
||||
<div class="tag-manage">
|
||||
<div class="title">
|
||||
<span>已选择</span>
|
||||
</div>
|
||||
|
||||
<div class="tag-groups">
|
||||
<p
|
||||
v-for="(tag, i) in tags"
|
||||
v-show="tag.isSelectd"
|
||||
:key="i"
|
||||
class="tag-item"
|
||||
>
|
||||
<span>{{ tag.name }}</span>
|
||||
<i class="el-icon-close" @click="active(tag.id, tag.isSelectd)" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="title">
|
||||
<span>标签栏</span>
|
||||
</div>
|
||||
|
||||
<div class="tag-groups">
|
||||
<p
|
||||
v-for="(tag, i) in tags"
|
||||
:key="i"
|
||||
class="tag-item"
|
||||
:class="{ active: tag.isSelectd }"
|
||||
@click="active(tag.id, tag.isSelectd)"
|
||||
>
|
||||
<span>{{ tag.name }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<el-button
|
||||
v-show="!isInput"
|
||||
type="primary"
|
||||
class="addbtn"
|
||||
@click="isInput = !isInput"
|
||||
>+ 添加标签
|
||||
</el-button>
|
||||
|
||||
<div class="tag-input" v-show="isInput">
|
||||
<input
|
||||
ref="editTaginput"
|
||||
type="text"
|
||||
placeholder="回车保存..."
|
||||
v-model.trim="tagText"
|
||||
@keyup.enter="save"
|
||||
/>
|
||||
|
||||
<el-button type="primary" size="small" @click="isInput = false"
|
||||
>取消编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ServeUpdateArticleTag, ServeEditArticleTag } from '@/api/article'
|
||||
export default {
|
||||
name: 'NoteTagBox',
|
||||
props: {
|
||||
note_id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
tag_ids: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ids: [],
|
||||
isInput: false,
|
||||
tagText: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.ids = this.tag_ids.map(tag => tag.id)
|
||||
},
|
||||
computed: {
|
||||
tags() {
|
||||
let tags = []
|
||||
this.$store.state.note.tags.forEach(tag => {
|
||||
tags.push({
|
||||
id: tag.id,
|
||||
name: tag.tag_name,
|
||||
isSelectd: this.ids.includes(tag.id),
|
||||
})
|
||||
})
|
||||
|
||||
return tags
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 设置笔记标签事件
|
||||
active(tag_id, isSelect) {
|
||||
if (isSelect) {
|
||||
this.ids.forEach((item, index) => {
|
||||
if (item == tag_id) {
|
||||
this.ids.splice(index, 1)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.ids.push(tag_id)
|
||||
}
|
||||
|
||||
ServeUpdateArticleTag({
|
||||
article_id: this.note_id,
|
||||
tags: this.getSelectTags(),
|
||||
})
|
||||
},
|
||||
|
||||
// 获取选中的标签ids
|
||||
getSelectTags() {
|
||||
let ids = []
|
||||
for (let item of this.tags) {
|
||||
if (item.isSelectd) ids.push(item.id)
|
||||
}
|
||||
|
||||
return ids
|
||||
},
|
||||
|
||||
// 保存标签事件
|
||||
save() {
|
||||
let tag_name = this.tagText
|
||||
ServeEditArticleTag({
|
||||
tag_id: 0,
|
||||
tag_name,
|
||||
}).then(({ code, data }) => {
|
||||
if (code !== 200) return false
|
||||
|
||||
this.$store.commit('PUSH_NOTE_TAG', {
|
||||
id: data.id,
|
||||
tag_name: tag_name,
|
||||
count: 0,
|
||||
})
|
||||
|
||||
this.tagText = ''
|
||||
this.isInput = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.tag-manage {
|
||||
.title {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
color: #ccc;
|
||||
border-bottom: 1px solid #f0e9e9;
|
||||
padding-bottom: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tag-groups {
|
||||
padding: 8px 8px 8px 0;
|
||||
cursor: pointer;
|
||||
|
||||
.tag-item {
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
margin: 0 3px 5px 0;
|
||||
color: #409eff;
|
||||
background: rgba(64, 158, 255, 0.1);
|
||||
border-radius: 1px;
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: #70b5fb;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addbtn {
|
||||
height: 33px;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
line-height: 9px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tag-input {
|
||||
margin-top: 20px;
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
width: 200px;
|
||||
height: 30px;
|
||||
border: 1px solid #66b1ff;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
margin-right: 5px;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<svg :class="svgClass" aria-hidden="true" v-on="$listeners">
|
||||
<use :xlink:href="iconName" />
|
||||
</svg>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'svg-icon',
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
iconName() {
|
||||
return `#icon-${this.iconClass}`
|
||||
},
|
||||
svgClass() {
|
||||
if (this.className) {
|
||||
return 'svg-icon ' + this.className
|
||||
} else {
|
||||
return 'svg-icon'
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.svg-icon {
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -1,529 +0,0 @@
|
||||
<template>
|
||||
<div v-show="isShow" class="lum-dialog-mask animated fadeIn">
|
||||
<el-container class="container" v-outside="close">
|
||||
<el-header class="no-padding header" height="180px">
|
||||
<i class="close el-icon-error pointer" @click="close" />
|
||||
<div class="img-banner">
|
||||
<img :src="userInfo.imgbag" class="img-banner" />
|
||||
</div>
|
||||
<div class="user-header">
|
||||
<div class="avatar">
|
||||
<div class="avatar-box">
|
||||
<img
|
||||
:src="userInfo.avatar"
|
||||
:onerror="$store.state.detaultAvatar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nickname">
|
||||
<i class="iconfont icon-qianming" />
|
||||
<span>{{ userInfo.nickname || '未设置昵称' }}</span>
|
||||
<div class="share no-select" @click="contacts = true">
|
||||
<i class="iconfont icon-fenxiang3" /> <span>分享</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main class="no-padding main">
|
||||
<div class="user-sign">
|
||||
<div class="sign-arrow"></div>
|
||||
<i class="iconfont icon-bianji" />
|
||||
<span>编辑个签,展示我的独特态度。 </span>
|
||||
</div>
|
||||
|
||||
<div class="card-rows no-select">
|
||||
<div class="card-row">
|
||||
<label>手机</label>
|
||||
<span>{{ mobile(userInfo.mobile) }}</span>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<label>昵称</label>
|
||||
<span>{{ userInfo.nickname || '未设置昵称' }}</span>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<label>性别</label>
|
||||
<span v-if="userInfo.gender == 1">男</span>
|
||||
<span v-else-if="userInfo.gender == 2">女</span>
|
||||
<span v-else>未知</span>
|
||||
</div>
|
||||
<div v-show="userInfo.friendStatus == 2" class="card-row">
|
||||
<label>备注</label>
|
||||
<span v-if="!editRemark.isShow">
|
||||
{{
|
||||
userInfo.nicknameRemark ? userInfo.nicknameRemark : '暂无备注'
|
||||
}}
|
||||
</span>
|
||||
<span v-else>
|
||||
<input
|
||||
v-model="editRemark.text"
|
||||
v-focus
|
||||
class="friend-remark"
|
||||
type="text"
|
||||
@keyup.enter="editRemarkSubmit"
|
||||
/>
|
||||
</span>
|
||||
<i
|
||||
v-show="!editRemark.isShow"
|
||||
class="el-icon-edit-outline"
|
||||
@click="clickEditRemark"
|
||||
/>
|
||||
</div>
|
||||
<div class="card-row">
|
||||
<label>邮箱</label>
|
||||
<span>未设置</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
<el-footer
|
||||
v-show="userInfo.friendStatus !== 0"
|
||||
class="no-padding footer"
|
||||
height="50px"
|
||||
>
|
||||
<el-button
|
||||
v-if="userInfo.friendStatus == 1 && userInfo.friendApply == 0"
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-circle-plus-outline"
|
||||
@click="applyFrom.isShow = true"
|
||||
>添加好友
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="userInfo.friendApply == 1"
|
||||
type="primary"
|
||||
size="small"
|
||||
>已发送好友申请,请耐心等待...
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="userInfo.friendStatus == 2"
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-s-promotion"
|
||||
@click="sendMessage(userInfo)"
|
||||
>发消息
|
||||
</el-button>
|
||||
</el-footer>
|
||||
|
||||
<!-- 添加好友申请表单 -->
|
||||
<div
|
||||
v-outside="closeApplyFrom"
|
||||
class="friend-from"
|
||||
:class="{ 'friend-from-show': applyFrom.isShow }"
|
||||
>
|
||||
<p>
|
||||
<span>请填写好友申请备注:</span>
|
||||
<span @click="closeApplyFrom">取消</span>
|
||||
</p>
|
||||
<div>
|
||||
<input
|
||||
v-model="applyFrom.text"
|
||||
type="text"
|
||||
placeholder="(必填项)"
|
||||
@keyup.enter="sendApply"
|
||||
/>
|
||||
<el-button type="primary" size="small" @click="sendApply">
|
||||
立即提交
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-container>
|
||||
|
||||
<UserContacts
|
||||
v-if="contacts"
|
||||
@confirm="confirmContact"
|
||||
@close="contacts = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import UserContacts from '@/components/chat/UserContacts'
|
||||
import { ServeCreateTalkList } from '@/api/chat'
|
||||
import { ServeSearchUser } from '@/api/user'
|
||||
import { ServeCreateContact, ServeEditContactRemark } from '@/api/contacts'
|
||||
|
||||
export default {
|
||||
name: 'UserBusinessCard',
|
||||
components: {
|
||||
UserContacts,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
|
||||
// 用户ID
|
||||
user_id: 0,
|
||||
|
||||
// 用户相关信息
|
||||
userInfo: {
|
||||
mobile: '',
|
||||
nickname: '',
|
||||
avatar: '',
|
||||
motto: '',
|
||||
friendStatus: 0,
|
||||
friendApply: 0,
|
||||
nicknameRemark: '',
|
||||
|
||||
imgbag: require('@/assets/image/default-user-banner.png'),
|
||||
gender: 0, //[0:未知;1:男;2:女;默认0]
|
||||
},
|
||||
|
||||
// 好友备注表单
|
||||
editRemark: {
|
||||
isShow: false,
|
||||
text: '',
|
||||
},
|
||||
|
||||
// 好友申请表单
|
||||
applyFrom: {
|
||||
isShow: false,
|
||||
text: '',
|
||||
},
|
||||
|
||||
contacts: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 显示窗口
|
||||
open(user_id) {
|
||||
this.isShow = true
|
||||
this.user_id = user_id
|
||||
this.findUserDetail()
|
||||
},
|
||||
|
||||
// 关闭窗口
|
||||
close() {
|
||||
if (this.contacts) {
|
||||
return false
|
||||
}
|
||||
this.isShow = false
|
||||
},
|
||||
|
||||
// 手机号格式化
|
||||
mobile(mobile) {
|
||||
return (
|
||||
mobile.substr(0, 3) +
|
||||
' ' +
|
||||
mobile.substr(3, 4) +
|
||||
' ' +
|
||||
mobile.substr(7, 4)
|
||||
)
|
||||
},
|
||||
|
||||
// 点击编辑备注信息
|
||||
clickEditRemark() {
|
||||
this.editRemark.isShow = true
|
||||
this.editRemark.text = this.userInfo.nicknameRemark
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
findUserDetail() {
|
||||
ServeSearchUser({
|
||||
user_id: this.user_id,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
let data = res.data
|
||||
this.userInfo.user_id = data.id
|
||||
this.userInfo.mobile = data.mobile
|
||||
this.userInfo.nickname = data.nickname
|
||||
this.userInfo.nicknameRemark = data.nickname_remark
|
||||
this.userInfo.motto = data.motto
|
||||
this.userInfo.avatar = data.avatar
|
||||
this.userInfo.friendStatus = data.friend_status
|
||||
this.userInfo.friendApply = data.friend_apply
|
||||
this.userInfo.gender = data.gender
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 发送添加好友申请
|
||||
sendApply() {
|
||||
if (this.applyFrom.text == '') return
|
||||
ServeCreateContact({
|
||||
friend_id: this.userInfo.user_id,
|
||||
remarks: this.applyFrom.text,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.applyFrom.isShow = false
|
||||
this.applyFrom.text = ''
|
||||
this.userInfo.friendApply = 1
|
||||
} else {
|
||||
alert('发送好友申请失败,请稍后再试...')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 编辑好友备注信息
|
||||
editRemarkSubmit() {
|
||||
let data = {
|
||||
friend_id: this.userInfo.user_id,
|
||||
remarks: this.editRemark.text,
|
||||
}
|
||||
|
||||
if (data.remarks == this.userInfo.nicknameRemark) {
|
||||
this.editRemark.isShow = false
|
||||
return
|
||||
}
|
||||
|
||||
ServeEditContactRemark(data).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.editRemark.isShow = false
|
||||
this.userInfo.nicknameRemark = data.remarks
|
||||
this.$emit('changeRemark', data)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 隐藏申请表单
|
||||
closeApplyFrom() {
|
||||
this.applyFrom.isShow = false
|
||||
},
|
||||
|
||||
// 发送好友消息
|
||||
sendMessage() {
|
||||
let userInfo = this.userInfo
|
||||
ServeCreateTalkList({
|
||||
type: 1,
|
||||
receive_id: this.user_id,
|
||||
}).then(res => {
|
||||
if (res.code !== 200) return
|
||||
|
||||
this.$root.dumpTalkPage(`1_${userInfo.user_id}`)
|
||||
})
|
||||
},
|
||||
|
||||
confirmContact(array) {
|
||||
this.contacts = false
|
||||
this.$notify.info({
|
||||
title: '消息',
|
||||
message: '分享功能正在开发中...',
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: white;
|
||||
width: 350px;
|
||||
height: 600px;
|
||||
overflow: hidden;
|
||||
border-radius: 3px;
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
color: white;
|
||||
transition: all 1s;
|
||||
z-index: 1;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.img-banner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url(~@/assets/image/default-user-banner.png);
|
||||
background-size: 100%;
|
||||
transition: all 0.2s linear;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
img:hover {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
-webkit-filter: contrast(130%);
|
||||
filter: contrast(130%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
background-color: white;
|
||||
padding: 45px 16px 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-top: 1px solid #f5eeee;
|
||||
|
||||
button {
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-header {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.avatar {
|
||||
width: 100px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.avatar-box {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nickname {
|
||||
flex: auto;
|
||||
padding-top: 50px;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
|
||||
span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.share {
|
||||
display: inline-flex;
|
||||
width: 50px;
|
||||
height: 22px;
|
||||
background: #ff5722;
|
||||
color: white;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3px 8px;
|
||||
border-radius: 20px;
|
||||
transform: scale(0.7);
|
||||
cursor: pointer;
|
||||
i {
|
||||
margin-top: 2px;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-sign {
|
||||
min-height: 26px;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
line-height: 25px;
|
||||
background: #f3f5f7;
|
||||
color: #7d7d7d;
|
||||
font-size: 12px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
position: relative;
|
||||
|
||||
.sign-arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
border: 5px solid hsla(0, 0%, 96.9%, 0);
|
||||
border-bottom-color: #f3f5f7;
|
||||
left: 28px;
|
||||
top: -9px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-rows {
|
||||
.card-row {
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
color: #736f6f;
|
||||
|
||||
label {
|
||||
margin-right: 25px;
|
||||
color: #cbc5c5;
|
||||
}
|
||||
|
||||
.friend-remark {
|
||||
border-bottom: 1px dashed #bec3d0;
|
||||
padding-bottom: 2px;
|
||||
color: #736f6f;
|
||||
width: 60%;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.el-icon-edit-outline {
|
||||
margin-left: 3px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 好友申请表单 */
|
||||
.friend-from {
|
||||
position: absolute;
|
||||
background: #fbf6f6;
|
||||
height: 80px;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
bottom: -80px;
|
||||
left: 0;
|
||||
transition: all 0.5s ease-in-out;
|
||||
|
||||
p {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
padding: 7px 5px 5px 15px;
|
||||
font-size: 13px;
|
||||
|
||||
span {
|
||||
&:nth-child(2) {
|
||||
float: right;
|
||||
margin-right: 13px;
|
||||
color: #32caff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
height: 31px;
|
||||
line-height: 20px;
|
||||
padding: 7px 5px 5px 15px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
width: 220px;
|
||||
border-radius: 3px;
|
||||
padding: 0 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.friend-from-show {
|
||||
bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,139 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="user-card animated fadeIn">
|
||||
<div class="card-header">
|
||||
<img :src="$store.state.user.visitCardBag" class="no-select" />
|
||||
<div class="user-avatar no-select">
|
||||
<img
|
||||
:src="$store.state.user.avatar"
|
||||
:onerror="$store.state.detaultAvatar"
|
||||
/>
|
||||
</div>
|
||||
<div class="user-nickname">
|
||||
<i class="iconfont icon-qianming" />
|
||||
<span v-text="$store.state.user.nickname"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-main">
|
||||
<div class="usersign">
|
||||
<div class="arrow"></div>
|
||||
<span v-if="$store.state.user.signature">
|
||||
<span style="font-weight: 600">个性签名</span> :
|
||||
{{ $store.state.user.signature }}
|
||||
</span>
|
||||
<span v-else>
|
||||
<i class="iconfont icon-bianji" />
|
||||
<span>编辑个签,展示我的独特态度。</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UserCard',
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.user-card {
|
||||
width: 320px;
|
||||
min-height: 300px;
|
||||
background: #ffffff;
|
||||
box-shadow: -1px 1px 9px 0px #e6e3e3;
|
||||
padding-bottom: 10px;
|
||||
|
||||
.card-header {
|
||||
height: 230px;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
-webkit-transition: all 0.2s linear;
|
||||
transition: all 0.2s linear;
|
||||
|
||||
&:hover {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
-webkit-filter: contrast(130%);
|
||||
filter: contrast(130%);
|
||||
}
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
border: 5px solid #fff;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
top: -35px;
|
||||
margin-left: 15px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.user-nickname {
|
||||
position: relative;
|
||||
top: -72px;
|
||||
text-align: left;
|
||||
margin-left: 105px;
|
||||
margin-right: 5px;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
span {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-card .card-main {
|
||||
margin-top: 10px;
|
||||
min-height: 50px;
|
||||
text-align: left;
|
||||
padding: 0 16px;
|
||||
|
||||
.usersign {
|
||||
min-height: 26px;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
line-height: 25px;
|
||||
background: #f3f5f7;
|
||||
color: #7d7d7d;
|
||||
font-size: 12px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
.icon-bianji {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
border: solid 5px;
|
||||
top: 5px;
|
||||
border-color: rgba(247, 247, 247, 0) rgba(247, 247, 247, 0) #f3f5f7
|
||||
rgba(247, 247, 247, 0);
|
||||
top: -10px;
|
||||
left: 31px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,223 +0,0 @@
|
||||
<template>
|
||||
<div class="lum-dialog-mask" v-show="isShow">
|
||||
<el-container class="lum-dialog-box" v-outside="close">
|
||||
<el-header class="header" height="50px">
|
||||
<p>绑定邮箱</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="close" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item prop="email" label="邮箱">
|
||||
<el-input
|
||||
v-model="form.email"
|
||||
class="cuborder-radius"
|
||||
size="medium"
|
||||
placeholder="请填写邮箱地址"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="sms_code" label="验证码">
|
||||
<el-input
|
||||
v-model="form.sms_code"
|
||||
class="cuborder-radius"
|
||||
style="width: 185px"
|
||||
size="medium"
|
||||
maxlength="6"
|
||||
placeholder="邮件验证码"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
<div v-if="smsLock" class="code-btn disable">正在发送 ...</div>
|
||||
<div
|
||||
v-else-if="smsLock == false && smsLockObj.time == null"
|
||||
class="code-btn"
|
||||
@click="sendSms"
|
||||
>
|
||||
获取验证码
|
||||
</div>
|
||||
<div v-else class="code-btn disable">
|
||||
重新发送({{ smsLockObj.time }}s)
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password" label="密码">
|
||||
<el-input
|
||||
v-model="form.password"
|
||||
class="cuborder-radius no-border"
|
||||
type="password"
|
||||
size="medium"
|
||||
placeholder="登录密码验证"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-top: 40px">
|
||||
<el-button
|
||||
class="submit-btn"
|
||||
type="primary"
|
||||
size="medium"
|
||||
:loading="loading"
|
||||
@click="onSubmit('form')"
|
||||
>立即修改
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import SmsLock from '@/plugins/sms-lock'
|
||||
import { ServeSendEmailCode, ServeUpdateEmail } from '@/api/user'
|
||||
import { isEmail } from '@/utils/validate'
|
||||
|
||||
export default {
|
||||
name: 'UserEditEmail',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
form: {
|
||||
email: '',
|
||||
password: '',
|
||||
sms_code: '',
|
||||
},
|
||||
rules: {
|
||||
email: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入邮箱地址',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
type: 'email',
|
||||
message: '请输入正确的邮箱地址',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: '登录密码不能为空!',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
sms_code: [
|
||||
{
|
||||
required: true,
|
||||
message: '验证码不能为空!',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
smsLock: false,
|
||||
smsLockObj: null,
|
||||
isShow: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.smsLockObj = new SmsLock('CHANGE_EMAIL_SMS', 120)
|
||||
},
|
||||
destroyed() {
|
||||
this.smsLockObj.clearInterval()
|
||||
},
|
||||
methods: {
|
||||
// 显示窗口
|
||||
open() {
|
||||
this.$refs['form'].resetFields()
|
||||
this.isShow = true
|
||||
},
|
||||
|
||||
// 关闭窗口
|
||||
close() {
|
||||
this.isShow = false
|
||||
},
|
||||
|
||||
//点击发送邮件验证码
|
||||
sendSms() {
|
||||
if (!isEmail(this.form.email)) {
|
||||
this.$refs.form.validateField('email')
|
||||
return false
|
||||
}
|
||||
|
||||
this.smsLock = true
|
||||
ServeSendEmailCode({
|
||||
email: this.form.email,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.smsLockObj.start()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.smsLock = false
|
||||
})
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
onSubmit(formName) {
|
||||
this.$refs[formName].validate(valid => {
|
||||
if (!valid) return false
|
||||
this.changeEmail()
|
||||
})
|
||||
},
|
||||
|
||||
// 提交修改邮箱
|
||||
changeEmail() {
|
||||
this.loading = true
|
||||
ServeUpdateEmail({
|
||||
email: this.form.email,
|
||||
email_code: this.form.sms_code,
|
||||
password: this.form.password,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.$refs['form'].resetFields()
|
||||
this.$emit('success')
|
||||
this.close()
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '修改邮箱成功...',
|
||||
type: 'success',
|
||||
})
|
||||
} else {
|
||||
this.$message(res.message)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 450px;
|
||||
max-width: 450px;
|
||||
|
||||
.main {
|
||||
.code-btn {
|
||||
width: 140px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
display: inline-block;
|
||||
background: #f3ecec;
|
||||
text-align: center;
|
||||
color: #777373;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
margin-left: 5px;
|
||||
|
||||
&:active {
|
||||
background: #e4dbdb;
|
||||
}
|
||||
|
||||
&.disable {
|
||||
cursor: not-allowed !important;
|
||||
background: #f7f7f7 !important;
|
||||
color: silver !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,254 +0,0 @@
|
||||
<template>
|
||||
<div v-show="isShow" class="lum-dialog-mask">
|
||||
<el-container class="lum-dialog-box" v-outside="close">
|
||||
<el-header class="header" height="50px">
|
||||
<p>绑定手机</p>
|
||||
<p class="tools">
|
||||
<i class="close-btn el-icon-close" @click="close" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item prop="username" label="手机号">
|
||||
<el-input
|
||||
v-model="form.username"
|
||||
class="cuborder-radius"
|
||||
maxlength="11"
|
||||
size="medium"
|
||||
placeholder="请填写新手机号"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="sms_code" label="验证码">
|
||||
<el-input
|
||||
v-model="form.sms_code"
|
||||
class="cuborder-radius"
|
||||
style="width: 185px"
|
||||
maxlength="6"
|
||||
size="medium"
|
||||
placeholder="验证码"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
<div v-if="smsLock" class="send-code-btn disable">正在发送 ...</div>
|
||||
<div
|
||||
v-else-if="smsLock == false && smsLockObj.time == null"
|
||||
class="send-code-btn"
|
||||
@click="sendSms"
|
||||
>
|
||||
获取短信
|
||||
</div>
|
||||
<div v-else class="send-code-btn disable">
|
||||
重新发送({{ smsLockObj.time }}s)
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password" label="密码">
|
||||
<el-input
|
||||
v-model="form.password"
|
||||
class="cuborder-radius no-border"
|
||||
type="password"
|
||||
size="medium"
|
||||
placeholder="登录密码验证"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-top: 40px">
|
||||
<el-button
|
||||
class="submit-btn"
|
||||
type="primary"
|
||||
size="medium"
|
||||
:loading="loading"
|
||||
@click="onSubmit('form')"
|
||||
>立即修改
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { isMobile } from '@/utils/validate'
|
||||
import SmsLock from '@/plugins/sms-lock'
|
||||
import { ServeSendMobileCode, ServeUpdateMobile } from '@/api/user'
|
||||
|
||||
export default {
|
||||
name: 'UserEditMobile',
|
||||
data() {
|
||||
let validateMobile = (rule, value, callback) => {
|
||||
if (value === '') {
|
||||
callback(new Error('登录手机号不能为空!'))
|
||||
} else {
|
||||
if (!isMobile(value)) {
|
||||
callback(new Error('登录手机号格式不正确!'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
loading: false,
|
||||
form: {
|
||||
username: '',
|
||||
password: '',
|
||||
sms_code: '',
|
||||
},
|
||||
rules: {
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
validator: validateMobile,
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
min: 11,
|
||||
max: 11,
|
||||
message: '手机号格式不正确!',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: '登录密码不能为空!',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
sms_code: [
|
||||
{
|
||||
required: true,
|
||||
message: '验证码不能为空!',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
smsLock: false,
|
||||
smsLockObj: null,
|
||||
isShow: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.smsLockObj = new SmsLock('EDIT_MOBILE_SMS', 120)
|
||||
},
|
||||
destroyed() {
|
||||
this.smsLockObj.clearInterval()
|
||||
},
|
||||
methods: {
|
||||
// 显示窗口
|
||||
open() {
|
||||
this.$refs['form'].resetFields()
|
||||
this.isShow = true
|
||||
},
|
||||
|
||||
// 关闭窗口
|
||||
close() {
|
||||
this.isShow = false
|
||||
},
|
||||
|
||||
//点击发送验证码
|
||||
sendSms() {
|
||||
if (!isMobile(this.form.username)) {
|
||||
this.$refs.form.validateField('username')
|
||||
return false
|
||||
}
|
||||
|
||||
this.smsLock = true
|
||||
ServeSendMobileCode({
|
||||
mobile: this.form.username,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code !== 200) {
|
||||
this.$notify({
|
||||
title: '提示',
|
||||
message: res.message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.smsLockObj.start()
|
||||
if (res.data.is_debug) {
|
||||
this.form.sms_code = res.data.sms_code
|
||||
setTimeout(() => {
|
||||
this.$notify({
|
||||
title: '提示',
|
||||
message: '已自动填充验证码',
|
||||
type: 'success',
|
||||
})
|
||||
this.form.sms_code = res.data.sms_code
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.smsLock = false
|
||||
})
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
onSubmit(formName) {
|
||||
this.$refs[formName].validate(valid => {
|
||||
if (!valid) return false
|
||||
this.changeMobile()
|
||||
})
|
||||
},
|
||||
|
||||
// 提交修改手机号
|
||||
changeMobile() {
|
||||
this.loading = true
|
||||
ServeUpdateMobile({
|
||||
mobile: this.form.username,
|
||||
sms_code: this.form.sms_code,
|
||||
password: this.form.password,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.$refs['form'].resetFields()
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '更换手机号成功...',
|
||||
type: 'success',
|
||||
})
|
||||
this.$emit('success')
|
||||
this.close()
|
||||
} else {
|
||||
this.$message(res.message)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 450px;
|
||||
max-width: 450px;
|
||||
|
||||
.main {
|
||||
.send-code-btn {
|
||||
width: 140px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
display: inline-block;
|
||||
background: #f3ecec;
|
||||
text-align: center;
|
||||
color: #777373;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
margin-left: 5px;
|
||||
|
||||
&:active {
|
||||
background: #e4dbdb;
|
||||
}
|
||||
|
||||
&.disable {
|
||||
cursor: not-allowed !important;
|
||||
background: #f7f7f7 !important;
|
||||
color: silver !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,161 +0,0 @@
|
||||
<template>
|
||||
<!-- 用户密码修改组件 -->
|
||||
<div class="lum-dialog-mask" v-show="isShow">
|
||||
<el-container class="lum-dialog-box" v-outside="close">
|
||||
<el-header class="header" height="50px">
|
||||
<p>修改密码</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="close" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item prop="old_password" label="旧密码">
|
||||
<el-input
|
||||
v-model="form.old_password"
|
||||
class="cuborder-radius no-border"
|
||||
type="password"
|
||||
size="medium"
|
||||
placeholder="请填写旧密码"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="new_password" label="新密码">
|
||||
<el-input
|
||||
v-model="form.new_password"
|
||||
class="cuborder-radius no-border"
|
||||
type="password"
|
||||
size="medium"
|
||||
placeholder="请填写新的密码"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="new_password2" label="重复密码">
|
||||
<el-input
|
||||
v-model="form.new_password2"
|
||||
class="cuborder-radius no-border"
|
||||
size="medium"
|
||||
type="password"
|
||||
placeholder="请再次填写新密码"
|
||||
@keyup.enter.native="onSubmit('form')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-top: 40px">
|
||||
<el-button
|
||||
class="submit-btn"
|
||||
type="primary"
|
||||
size="medium"
|
||||
:loading="loading"
|
||||
@click="onSubmit('form')"
|
||||
>立即修改
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ServeUpdatePassword } from '@/api/user'
|
||||
|
||||
export default {
|
||||
name: 'UserEditPassword',
|
||||
data() {
|
||||
var validatePass2 = (rule, value, callback) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'))
|
||||
} else if (value !== this.form.new_password) {
|
||||
callback(new Error('两次输入密码不一致!'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
loading: false,
|
||||
form: {
|
||||
old_password: '',
|
||||
new_password: '',
|
||||
new_password2: '',
|
||||
},
|
||||
rules: {
|
||||
old_password: [
|
||||
{
|
||||
required: true,
|
||||
message: '旧密码不能为空!',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
new_password: [
|
||||
{
|
||||
required: true,
|
||||
message: '新密码不能为空!',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
new_password2: [
|
||||
{
|
||||
required: true,
|
||||
validator: validatePass2,
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
isShow: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 显示窗口
|
||||
open() {
|
||||
this.$refs['form'].resetFields()
|
||||
this.isShow = true
|
||||
},
|
||||
|
||||
// 关闭窗口
|
||||
close() {
|
||||
this.isShow = false
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
onSubmit(formName) {
|
||||
this.$refs[formName].validate(valid => {
|
||||
if (!valid) return false
|
||||
this.changePassword()
|
||||
})
|
||||
},
|
||||
|
||||
// 提交修改手机号
|
||||
changePassword() {
|
||||
this.loading = true
|
||||
ServeUpdatePassword({
|
||||
old_password: this.form.old_password,
|
||||
new_password: this.form.new_password,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.$refs['form'].resetFields()
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '密码修改成功...',
|
||||
type: 'success',
|
||||
})
|
||||
} else {
|
||||
this.$message(res.message)
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 450px;
|
||||
max-width: 450px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,126 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="lum-dialog-mask" v-show="isShow">
|
||||
<el-container class="lum-dialog-box" v-outside="close">
|
||||
<el-header class="header" height="50px">
|
||||
<p>添加好友</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="close" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main">
|
||||
<el-input
|
||||
v-model="mobile"
|
||||
id="serach-mobile"
|
||||
class="input"
|
||||
prefix-icon="el-icon-search"
|
||||
placeholder="请输入对方手机号(精确查找)"
|
||||
clearable
|
||||
@keyup.enter.native="onSubmit"
|
||||
@input="error = false"
|
||||
/>
|
||||
<p v-show="error" class="error">
|
||||
无法找到该用户,请检查搜索内容并重试
|
||||
</p>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="loading"
|
||||
@click="onSubmit"
|
||||
>立即查找
|
||||
</el-button>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
|
||||
<!-- 查看好友用户信息 -->
|
||||
<UserBusinessCard ref="userBusinessCard" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ServeSearchContact } from '@/api/contacts'
|
||||
import UserBusinessCard from '@/components/user/UserBusinessCard'
|
||||
|
||||
export default {
|
||||
name: 'UserSearch',
|
||||
components: {
|
||||
UserBusinessCard,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
isShow: false,
|
||||
mobile: '',
|
||||
error: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 显示窗口
|
||||
open() {
|
||||
this.mobile = ''
|
||||
this.isShow = true
|
||||
this.$nextTick(() => {
|
||||
document.getElementById('serach-mobile').focus()
|
||||
})
|
||||
},
|
||||
|
||||
// 关闭窗口
|
||||
close() {
|
||||
this.isShow = false
|
||||
},
|
||||
|
||||
onSubmit() {
|
||||
let { mobile } = this
|
||||
|
||||
if (mobile == '') return false
|
||||
|
||||
this.loading = true
|
||||
ServeSearchContact({
|
||||
mobile,
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 200) {
|
||||
this.$refs.userBusinessCard.open(res.data.id)
|
||||
this.close()
|
||||
} else {
|
||||
this.error = true
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 450px;
|
||||
max-width: 450px;
|
||||
height: 250px;
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.input {
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
.error {
|
||||
width: 85%;
|
||||
color: red;
|
||||
font-size: 12px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 20px;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +0,0 @@
|
||||
export default {
|
||||
WEBSITE_NAME: process.env.VUE_APP_WEBSITE_NAME || 'CPS',
|
||||
BASE_API_URL: process.env.VUE_APP_API_BASE_URL || '',
|
||||
BASE_WWW_URL: process.env.VUE_APP_WWW_BASE_URL || '',
|
||||
BASE_WS_URL: process.env.VUE_APP_WEB_SOCKET_URL || '',
|
||||
WS_DEBUG: false,
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import Clickoutside from 'element-ui/src/utils/clickoutside'
|
||||
|
||||
// 自定义聚焦指令
|
||||
Vue.directive('focus', {
|
||||
inserted(el) {
|
||||
el.focus()
|
||||
},
|
||||
})
|
||||
|
||||
// 自定义粘贴指令
|
||||
Vue.directive('paste', {
|
||||
bind(el, binding, vnode) {
|
||||
el.addEventListener('paste', function(event) {
|
||||
//这里直接监听元素的粘贴事件
|
||||
binding.value(event)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// 自定义拖拽指令
|
||||
Vue.directive('drag', {
|
||||
bind(el, binding, vnode) {
|
||||
// 因为拖拽还包括拖动时的经过事件,离开事件,和进入事件,放下事件,
|
||||
// 浏览器对于拖拽的默认事件的处理是打开拖进来的资源,
|
||||
// 所以要先对这三个事件进行默认事件的禁止
|
||||
el.addEventListener('dragenter', function(event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
})
|
||||
el.addEventListener('dragover', function(event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
})
|
||||
el.addEventListener('dragleave', function(event) {
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
})
|
||||
el.addEventListener('drop', function(event) {
|
||||
// 这里阻止默认事件,并绑定事件的对象,用来在组件上返回事件对象
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
binding.value(event)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// 点击其他地方隐藏指令
|
||||
Vue.directive('outside', Clickoutside)
|
||||
@@ -1,2 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Custom icon list
|
||||
* All icons are loaded here for easy management
|
||||
*
|
||||
* 自定义图标加载表
|
||||
* 所有图标均从这里加载,方便管理
|
||||
*/
|
||||
import SvgMentionDown from '@/icons/svg/mention-down.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgNotFount from '@/icons/svg/not-fount.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgNote from '@/icons/svg/note.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgNoteBook from '@/icons/svg/note-book.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgNotData from '@/icons/svg/not-data.svg?inline' // path to your '*.svg?inline' file.
|
||||
import SvgZhuangFa from '@/icons/svg/zhuangfa.svg?inline' // path to your '*.svg?inline' file.
|
||||
|
||||
export {
|
||||
SvgMentionDown,
|
||||
SvgNotFount,
|
||||
SvgNote,
|
||||
SvgNoteBook,
|
||||
SvgNotData,
|
||||
SvgZhuangFa,
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
|
||||
import {
|
||||
Notification,
|
||||
Popover,
|
||||
Switch,
|
||||
Dropdown,
|
||||
DropdownMenu,
|
||||
DropdownItem,
|
||||
Message,
|
||||
Container,
|
||||
Header,
|
||||
Aside,
|
||||
Main,
|
||||
Footer,
|
||||
Menu,
|
||||
Submenu,
|
||||
MenuItem,
|
||||
MenuItemGroup,
|
||||
Button,
|
||||
Image,
|
||||
Loading,
|
||||
Row,
|
||||
Col,
|
||||
MessageBox,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
Divider,
|
||||
Link,
|
||||
Tooltip,
|
||||
Autocomplete,
|
||||
Scrollbar,
|
||||
Avatar,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Progress,
|
||||
Dialog,
|
||||
Pagination,
|
||||
Table,
|
||||
TableColumn,
|
||||
Tag,
|
||||
Select,
|
||||
Option,
|
||||
Upload,
|
||||
CheckboxGroup,
|
||||
Checkbox,
|
||||
Tabs,
|
||||
TabPane,
|
||||
RadioButton,
|
||||
Alert,
|
||||
Card,
|
||||
Tree,
|
||||
TimePicker,
|
||||
DatePicker,
|
||||
Badge,
|
||||
} from 'element-ui'
|
||||
|
||||
Vue.use(DatePicker)
|
||||
Vue.use(TimePicker)
|
||||
Vue.use(Badge)
|
||||
Vue.use(Tree)
|
||||
Vue.use(Card)
|
||||
Vue.use(Alert)
|
||||
Vue.use(RadioButton)
|
||||
Vue.use(Tabs)
|
||||
Vue.use(TabPane)
|
||||
Vue.use(CheckboxGroup)
|
||||
Vue.use(Checkbox)
|
||||
Vue.use(Upload)
|
||||
Vue.use(Option)
|
||||
Vue.use(Select)
|
||||
Vue.use(Tag)
|
||||
Vue.use(Pagination)
|
||||
Vue.use(Table)
|
||||
Vue.use(TableColumn)
|
||||
Vue.use(Popover)
|
||||
Vue.use(Switch)
|
||||
Vue.use(Dropdown)
|
||||
Vue.use(DropdownMenu)
|
||||
Vue.use(DropdownItem)
|
||||
Vue.use(Container)
|
||||
Vue.use(Header)
|
||||
Vue.use(Aside)
|
||||
Vue.use(Main)
|
||||
Vue.use(Footer)
|
||||
Vue.use(Menu)
|
||||
Vue.use(Submenu)
|
||||
Vue.use(MenuItem)
|
||||
Vue.use(MenuItemGroup)
|
||||
Vue.use(Button)
|
||||
Vue.use(Image)
|
||||
Vue.use(Row)
|
||||
Vue.use(Col)
|
||||
Vue.use(Input)
|
||||
Vue.use(Form)
|
||||
Vue.use(FormItem)
|
||||
Vue.use(Divider)
|
||||
Vue.use(Link)
|
||||
Vue.use(Tooltip)
|
||||
Vue.use(Autocomplete)
|
||||
Vue.use(Scrollbar)
|
||||
Vue.use(Avatar)
|
||||
Vue.use(Radio)
|
||||
Vue.use(RadioGroup)
|
||||
Vue.use(Progress)
|
||||
Vue.use(Dialog)
|
||||
Vue.use(Loading.directive)
|
||||
|
||||
Vue.prototype.$notify = Notification
|
||||
Vue.prototype.$message = Message
|
||||
Vue.prototype.$confirm = MessageBox.confirm
|
||||
Vue.prototype.$prompt = MessageBox.prompt
|
||||
Vue.prototype.$alert = MessageBox.alert
|
||||
|
||||
import Contextmenu from 'vue-contextmenujs'
|
||||
Vue.use(Contextmenu)
|
||||
|
||||
process.env.NODE_ENV !== 'production' &&
|
||||
console.warn('[Lumen-IM] NOTICE: element-ui use lazy-load.')
|
||||
@@ -1,47 +0,0 @@
|
||||
import { copyTextToClipboard as Clipboard } from '@/utils/functions'
|
||||
|
||||
const copyFunc = (pre, text) => {
|
||||
let el = document.createElement('p')
|
||||
el.className = 'fz-btn'
|
||||
el.innerText = '复制'
|
||||
el.onclick = () => {
|
||||
Clipboard(text.replace(/(^\s*)|(\s*$)/g, ''), function() {
|
||||
el.innerText = '复制成功!'
|
||||
setTimeout(() => {
|
||||
el.innerText = '复制'
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
pre.appendChild(el)
|
||||
}
|
||||
|
||||
const preNmae = (pre, lang) => {
|
||||
let el = document.createElement('p')
|
||||
el.className = 'lang-name'
|
||||
el.innerText = lang
|
||||
pre.appendChild(el)
|
||||
}
|
||||
|
||||
function updateNodes(el, binding, vnode) {
|
||||
let preNodes = el.querySelectorAll('pre')
|
||||
preNodes.forEach(elPre => {
|
||||
let elCode = elPre.querySelector('code')
|
||||
let className = elCode.className
|
||||
let language = className.split('-')[1]
|
||||
|
||||
copyFunc(elPre, elCode.innerText)
|
||||
|
||||
if (language != undefined) {
|
||||
preNmae(elPre, language)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 代码格式化
|
||||
*/
|
||||
export default {
|
||||
bind: updateNodes,
|
||||
update: updateNodes,
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
@@ -1,9 +0,0 @@
|
||||
import Vue from 'vue';
|
||||
import SvgIcon from '@/components/svg-icon'; // svg component
|
||||
|
||||
// register globally
|
||||
Vue.component('svg-icon', SvgIcon);
|
||||
|
||||
const req = require.context('./svg', false, /\.svg$/);
|
||||
const requireAll = requireContext => requireContext.keys().map(requireContext);
|
||||
requireAll(req);
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |