mirror of
https://github.com/marktext/marktext.git
synced 2025-05-16 08:50:42 +08:00
feat: doutu
This commit is contained in:
parent
fdf4873399
commit
ca9fc76d2b
@ -1,9 +1,11 @@
|
|||||||
### 0.6.10
|
### 0.6.11
|
||||||
|
|
||||||
**Features**
|
**Features**
|
||||||
|
|
||||||
- Add **dark** theme and **light** theme in both realtime preview mode and source code mode.
|
- Add **dark** theme and **light** theme in both realtime preview mode and source code mode.
|
||||||
|
|
||||||
|
- Insert `doutu` into the document, use CMD + / to open the panel.
|
||||||
|
|
||||||
**Optimization**
|
**Optimization**
|
||||||
|
|
||||||
- Customize the scroll bar background color and thumb color.
|
- Customize the scroll bar background color and thumb color.
|
||||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "marktext",
|
"name": "marktext",
|
||||||
"version": "0.3.1",
|
"version": "0.6.10",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -550,11 +550,11 @@
|
|||||||
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
|
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
|
||||||
},
|
},
|
||||||
"axios": {
|
"axios": {
|
||||||
"version": "0.16.2",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
|
||||||
"integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=",
|
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"follow-redirects": "1.2.6",
|
"follow-redirects": "1.4.1",
|
||||||
"is-buffer": "1.1.6"
|
"is-buffer": "1.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -5387,9 +5387,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.2.6",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz",
|
||||||
"integrity": "sha512-FrMqZ/FONtHnbqO651UPpfRUVukIEwJhXMfdr/JWAmrDbeYBu773b1J6gdWDyRIj4hvvzQEHoEOTrdR8o6KLYA==",
|
"integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "3.1.0"
|
"debug": "3.1.0"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "marktext",
|
"name": "marktext",
|
||||||
"version": "0.6.10",
|
"version": "0.6.11",
|
||||||
"author": "Jocs <luoran1988@126.com>",
|
"author": "Jocs <luoran1988@126.com>",
|
||||||
"description": "A markdown editor",
|
"description": "A markdown editor",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -68,7 +68,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.16.1",
|
"axios": "^0.18.0",
|
||||||
"cheerio": "^1.0.0-rc.2",
|
"cheerio": "^1.0.0-rc.2",
|
||||||
"codemirror": "^5.31.0",
|
"codemirror": "^5.31.0",
|
||||||
"css-tree": "^1.0.0-alpha.27",
|
"css-tree": "^1.0.0-alpha.27",
|
||||||
|
@ -175,6 +175,35 @@ const formatCtrl = ContentState => {
|
|||||||
block.text = generator(tokens)
|
block.text = generator(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContentState.prototype.insertAidou = function (url) {
|
||||||
|
const { start, end } = this.cursor
|
||||||
|
const { key, offset: startOffset } = start
|
||||||
|
const { offset: endOffset } = end
|
||||||
|
const block = this.getBlock(key)
|
||||||
|
const { text } = block
|
||||||
|
if (key !== end.key) {
|
||||||
|
block.text = text.substring(0, startOffset) + `` + text.substring(startOffset)
|
||||||
|
const offset = startOffset + 2
|
||||||
|
this.cursor = {
|
||||||
|
start: { key, offset },
|
||||||
|
end: { key, offset }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
block.text = text.substring(0, start.offset) + `` + text.substring(end.offset)
|
||||||
|
this.cursor = {
|
||||||
|
start: {
|
||||||
|
key,
|
||||||
|
offset: startOffset + 2
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
key,
|
||||||
|
offset: startOffset + 2 + text.substring(startOffset, endOffset).length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
ContentState.prototype.format = function (type) {
|
ContentState.prototype.format = function (type) {
|
||||||
const { start, end } = selection.getCursorRange()
|
const { start, end } = selection.getCursorRange()
|
||||||
const startBlock = this.getBlock(start.key)
|
const startBlock = this.getBlock(start.key)
|
||||||
|
@ -454,6 +454,10 @@ class Aganippe {
|
|||||||
this.contentState.format(type)
|
this.contentState.format(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insertAidou (url) {
|
||||||
|
this.contentState.insertAidou(url)
|
||||||
|
}
|
||||||
|
|
||||||
search (value, opt) {
|
search (value, opt) {
|
||||||
const { selectHighlight } = opt
|
const { selectHighlight } = opt
|
||||||
this.contentState.search(value, opt)
|
this.contentState.search(value, opt)
|
||||||
|
@ -16,6 +16,9 @@ const createWindow = (pathname, options = {}) => {
|
|||||||
|
|
||||||
const { x, y, width, height } = mainWindowState
|
const { x, y, width, height } = mainWindowState
|
||||||
const winOpt = Object.assign({ x, y, width, height }, {
|
const winOpt = Object.assign({ x, y, width, height }, {
|
||||||
|
webPreferences: {
|
||||||
|
webSecurity: false
|
||||||
|
},
|
||||||
useContentSize: true,
|
useContentSize: true,
|
||||||
show: false,
|
show: false,
|
||||||
frame: false,
|
frame: false,
|
||||||
|
@ -58,5 +58,13 @@ export default {
|
|||||||
click: (menuItem, browserWindow) => {
|
click: (menuItem, browserWindow) => {
|
||||||
actions.edit(browserWindow, 'replace')
|
actions.edit(browserWindow, 'replace')
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
type: 'separator'
|
||||||
|
}, {
|
||||||
|
label: 'Aidou',
|
||||||
|
accelerator: 'CmdOrCtrl+/',
|
||||||
|
click: (menuItem, browserWindow) => {
|
||||||
|
actions.edit(browserWindow, 'aidou')
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,14 @@ export default {
|
|||||||
submenu: [{
|
submenu: [{
|
||||||
label: 'Dark',
|
label: 'Dark',
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
checked: true,
|
checked: false,
|
||||||
click (menuItem, browserWindow) {
|
click (menuItem, browserWindow) {
|
||||||
actions.selectTheme(browserWindow, 'dark')
|
actions.selectTheme(browserWindow, 'dark')
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
label: 'Light',
|
label: 'Light',
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
checked: false,
|
checked: true,
|
||||||
click (menuItem, browserWindow) {
|
click (menuItem, browserWindow) {
|
||||||
actions.selectTheme(browserWindow, 'light')
|
actions.selectTheme(browserWindow, 'light')
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -54,11 +54,15 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<aidou
|
||||||
|
@select="handleSelect"
|
||||||
|
></aidou>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Aganippe from '../../editor'
|
import Aganippe from '../../editor'
|
||||||
|
import aidou from './aidou/aidou.vue'
|
||||||
import bus from '../bus'
|
import bus from '../bus'
|
||||||
import { animatedScrollTo } from '../../editor/utils'
|
import { animatedScrollTo } from '../../editor/utils'
|
||||||
|
|
||||||
@ -69,6 +73,9 @@
|
|||||||
]
|
]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
aidou
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
typewriter: {
|
typewriter: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -166,6 +173,9 @@
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleSelect (url) {
|
||||||
|
this.editor && this.editor.insertAidou(url)
|
||||||
|
},
|
||||||
handleSearch (value, opt) {
|
handleSearch (value, opt) {
|
||||||
const searchMatches = this.editor.search(value, opt)
|
const searchMatches = this.editor.search(value, opt)
|
||||||
this.$store.dispatch('SEARCH', searchMatches)
|
this.$store.dispatch('SEARCH', searchMatches)
|
||||||
|
184
src/renderer/components/aidou/aidou.vue
Normal file
184
src/renderer/components/aidou/aidou.vue
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<div class="aidou">
|
||||||
|
<el-dialog
|
||||||
|
:visible.sync="showAiDou"
|
||||||
|
:show-close="false"
|
||||||
|
:modal="true"
|
||||||
|
custom-class="ag-dialog-table"
|
||||||
|
width="610px"
|
||||||
|
|
||||||
|
>
|
||||||
|
<div slot="title" class="search-wrapper">
|
||||||
|
<svg class="icon" aria-hidden="true" @click="shuffle">
|
||||||
|
<use xlink:href="#icon-shuffle"></use>
|
||||||
|
</svg>
|
||||||
|
<input type="text" v-model="query" class="search" @keyup.13="search" ref="search">
|
||||||
|
<svg class="icon" aria-hidden="true" @click="search">
|
||||||
|
<use xlink:href="#icon-search"></use>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="image-container" ref="emojis">
|
||||||
|
<div class="img-wrapper" v-for="(emoji, index) of aiList" :key="index" @click="handleEmojiClick(emoji)">
|
||||||
|
<img :src="emoji.link" alt="" >
|
||||||
|
</div>
|
||||||
|
<loading v-if="aiLoading"></loading>
|
||||||
|
</div>
|
||||||
|
<!-- <div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="showAiDou = false" size="mini">
|
||||||
|
<svg class="icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#icon-close"></use>
|
||||||
|
</svg>
|
||||||
|
</el-button>
|
||||||
|
</div> -->
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import bus from '../../bus'
|
||||||
|
import loading from './loading.vue'
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import hotWords from './hotWords'
|
||||||
|
import resource from '../../store/resource'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
loading
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
showAiDou: false,
|
||||||
|
query: '',
|
||||||
|
page: 1,
|
||||||
|
size: 24,
|
||||||
|
bindScroll: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
bus.$on('aidou', this.handleShowAiDou)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
const container = this.$refs.emojis
|
||||||
|
container.removeEventListener('scroll', this.handlerScroll)
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState([
|
||||||
|
'aiLoading', 'aiList'
|
||||||
|
])
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async handleEmojiClick ({ link }) {
|
||||||
|
try {
|
||||||
|
const base64 = await resource.fetchImgToBase64(link)
|
||||||
|
const { url } = await resource.sm(base64)
|
||||||
|
this.$emit('select', url)
|
||||||
|
this.showAiDou = false
|
||||||
|
} catch (err) {
|
||||||
|
// todo handle error
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleShowAiDou () {
|
||||||
|
this.showAiDou = true
|
||||||
|
if (!this.bindScroll) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const container = this.$refs.emojis
|
||||||
|
container.addEventListener('scroll', this.handlerScroll)
|
||||||
|
this.bindScroll = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.search.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handlerScroll (event) {
|
||||||
|
const container = this.$refs.emojis
|
||||||
|
const { offsetHeight, scrollHeight, scrollTop } = container
|
||||||
|
if (scrollHeight - scrollTop - offsetHeight <= 100 && !this.aiLoading) {
|
||||||
|
this.loadMore()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
search () {
|
||||||
|
const { query, size } = this
|
||||||
|
const page = this.page = 1
|
||||||
|
const type = 'search'
|
||||||
|
const params = { query, size, page }
|
||||||
|
this.$store.dispatch('AI_SEARCH', { params, type })
|
||||||
|
},
|
||||||
|
loadMore () {
|
||||||
|
const { query, size, page } = this
|
||||||
|
const params = { query, size, page: page + 1 }
|
||||||
|
this.$store.dispatch('AI_SEARCH', { params, type: 'loadMore' })
|
||||||
|
},
|
||||||
|
shuffle () {
|
||||||
|
const luckWord = hotWords[Math.random() * hotWords.length | 0]
|
||||||
|
this.query = luckWord
|
||||||
|
this.search()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search-wrapper {
|
||||||
|
max-width: 410px;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
padding: 5px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 3px 8px rgba(0,0,0,.1);
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.search {
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0 8px;
|
||||||
|
margin: 0 10px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
.search-wrapper svg {
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0 5px;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
.image-container {
|
||||||
|
height: 410px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.image-container .img-wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 130px;
|
||||||
|
height: 130px;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all .25s ease-in-out;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.image-container .img-wrapper img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.image-container .img-wrapper:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
.image-container .img-wrapper:nth-of-type(4n) {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
.dialog-footer {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
117
src/renderer/components/aidou/hotWords.js
Normal file
117
src/renderer/components/aidou/hotWords.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
export default [
|
||||||
|
'bug',
|
||||||
|
'代码有毒',
|
||||||
|
'大佬',
|
||||||
|
'hentai',
|
||||||
|
'变态',
|
||||||
|
'40米',
|
||||||
|
'哈哈',
|
||||||
|
'嘿嘿',
|
||||||
|
'滑稽',
|
||||||
|
'原谅',
|
||||||
|
'喜欢',
|
||||||
|
'秋裤',
|
||||||
|
'盒子精',
|
||||||
|
'吃鸡',
|
||||||
|
'阿卡林',
|
||||||
|
'元旦快乐',
|
||||||
|
'新年快乐',
|
||||||
|
'康娜',
|
||||||
|
'赞',
|
||||||
|
'2b',
|
||||||
|
'正面上我',
|
||||||
|
'bilibili',
|
||||||
|
'胖次',
|
||||||
|
'飞机',
|
||||||
|
'哲学',
|
||||||
|
'鸡鸡',
|
||||||
|
'2233',
|
||||||
|
'233',
|
||||||
|
'蕾姆',
|
||||||
|
'拉姆',
|
||||||
|
'niconiconi',
|
||||||
|
'图样图森破',
|
||||||
|
'非战斗人员',
|
||||||
|
'天依',
|
||||||
|
'绅士',
|
||||||
|
'今天的风儿',
|
||||||
|
'战5渣',
|
||||||
|
'楼上',
|
||||||
|
'楼下',
|
||||||
|
'女装',
|
||||||
|
'吸猫',
|
||||||
|
'二次元',
|
||||||
|
'新吧唧',
|
||||||
|
'poi',
|
||||||
|
'提督',
|
||||||
|
'咸鱼',
|
||||||
|
'滴滴',
|
||||||
|
'老司机',
|
||||||
|
'香菜',
|
||||||
|
'金馆长',
|
||||||
|
'笑',
|
||||||
|
'机智',
|
||||||
|
'撩',
|
||||||
|
'套路',
|
||||||
|
'洪荒之力',
|
||||||
|
'傲娇',
|
||||||
|
'一言不合',
|
||||||
|
'百合',
|
||||||
|
'白学家',
|
||||||
|
'电学',
|
||||||
|
'白色相册',
|
||||||
|
'诚哥',
|
||||||
|
'本子',
|
||||||
|
'香蕉君',
|
||||||
|
'金坷垃',
|
||||||
|
'舰娘',
|
||||||
|
'圣杯',
|
||||||
|
'呆毛',
|
||||||
|
'咖喱棒',
|
||||||
|
'金闪闪',
|
||||||
|
'吃土',
|
||||||
|
'小目标',
|
||||||
|
'FFF团',
|
||||||
|
'友谊的小船',
|
||||||
|
'狗带',
|
||||||
|
'没想到你是这样的',
|
||||||
|
'懵逼',
|
||||||
|
'我好方',
|
||||||
|
'辣眼睛',
|
||||||
|
'猴赛雷',
|
||||||
|
'吓死宝宝了',
|
||||||
|
'宝宝',
|
||||||
|
'咋不上天',
|
||||||
|
'重要的事',
|
||||||
|
'城会玩',
|
||||||
|
'A4腰',
|
||||||
|
'城会玩',
|
||||||
|
'厉害了我的哥',
|
||||||
|
'感觉身体被掏空',
|
||||||
|
'互相伤害',
|
||||||
|
'北京瘫',
|
||||||
|
'葛优躺',
|
||||||
|
'一颗赛艇',
|
||||||
|
'因吹斯汀',
|
||||||
|
'醒醒',
|
||||||
|
'社会我',
|
||||||
|
'萝莉',
|
||||||
|
'御姐',
|
||||||
|
'正太',
|
||||||
|
'Loli',
|
||||||
|
'单身狗',
|
||||||
|
'逗比',
|
||||||
|
'坟头草',
|
||||||
|
'你开心',
|
||||||
|
'裤子都脱了',
|
||||||
|
'算我输',
|
||||||
|
'修仙',
|
||||||
|
'老哥稳',
|
||||||
|
'奶子',
|
||||||
|
'小姐姐',
|
||||||
|
'石乐志',
|
||||||
|
'皮皮虾我们走',
|
||||||
|
'我们走',
|
||||||
|
'小拳拳',
|
||||||
|
'把我的意大利'
|
||||||
|
]
|
94
src/renderer/components/aidou/loading.vue
Normal file
94
src/renderer/components/aidou/loading.vue
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cpt-loading">
|
||||||
|
<div class="loader">
|
||||||
|
<span v-for="i in 3" :key="i" :style="dotSize"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
default: 14
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
dotSize () {
|
||||||
|
const size = `${this.size}px`
|
||||||
|
return {
|
||||||
|
width: size,
|
||||||
|
height: size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.cpt-loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.loader {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader span {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: 3s infinite linear;
|
||||||
|
}
|
||||||
|
.loader span:nth-child(1) {
|
||||||
|
background: #F2F6FC;
|
||||||
|
animation: kiri 1.2s infinite linear;
|
||||||
|
}
|
||||||
|
.loader span:nth-child(2) {
|
||||||
|
z-index: 100;
|
||||||
|
background: #EBEEF5;
|
||||||
|
}
|
||||||
|
.loader span:nth-child(3) {
|
||||||
|
background: #C0C4CC;
|
||||||
|
animation: kanan 1.2s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes kanan {
|
||||||
|
0% {
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateX(-20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
z-index: 200;
|
||||||
|
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes kiri {
|
||||||
|
0% {
|
||||||
|
z-index: 200;
|
||||||
|
|
||||||
|
transform: translateX(-20px);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(-20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
38
src/renderer/store/aidou.js
Normal file
38
src/renderer/store/aidou.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import resource from './resource'
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
aiLoading: false,
|
||||||
|
aiList: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const mutations = {
|
||||||
|
SET_AI_LIST (state, { data, type }) {
|
||||||
|
console.log(type)
|
||||||
|
if (type === 'search') {
|
||||||
|
state.aiList = data
|
||||||
|
} else {
|
||||||
|
state.aiList = [...state.aiList, ...data]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SET_AI_STATUS (state, bool) {
|
||||||
|
state.aiLoading = bool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
AI_SEARCH ({ commit }, { params, type }) {
|
||||||
|
commit('SET_AI_STATUS', true)
|
||||||
|
return resource.sogou(params)
|
||||||
|
.then(({ data, total }) => {
|
||||||
|
commit('SET_AI_LIST', { data, type })
|
||||||
|
commit('SET_AI_STATUS', false)
|
||||||
|
return { data, total }
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err)
|
||||||
|
commit('SET_AI_STATUS', false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { state, mutations, actions }
|
@ -2,11 +2,13 @@ import Vue from 'vue'
|
|||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
import editorStore from './editor'
|
import editorStore from './editor'
|
||||||
|
import aidouStore from './aidou'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
const storeArray = [
|
const storeArray = [
|
||||||
editorStore
|
editorStore,
|
||||||
|
aidouStore
|
||||||
]
|
]
|
||||||
|
|
||||||
const { actions, mutations, state } = storeArray.reduce((acc, s) => {
|
const { actions, mutations, state } = storeArray.reduce((acc, s) => {
|
||||||
|
63
src/renderer/store/resource.js
Normal file
63
src/renderer/store/resource.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import { serialize, merge, dataURItoBlob } from '../util'
|
||||||
|
|
||||||
|
const CONFIG = {
|
||||||
|
SOGOU: {
|
||||||
|
API: 'https://pic.sogou.com/pics/json.jsp',
|
||||||
|
PARAMS: {
|
||||||
|
query: '',
|
||||||
|
st: 5,
|
||||||
|
start: 0,
|
||||||
|
xml_len: 100,
|
||||||
|
reqFrom: 'wap_result'
|
||||||
|
},
|
||||||
|
HOT_SEARCH: 'https://pic.sogou.com/pic/emo/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resource = {
|
||||||
|
sogou ({ query, page, size }) {
|
||||||
|
const api = CONFIG.SOGOU.API
|
||||||
|
const defParams = CONFIG.SOGOU.PARAMS
|
||||||
|
const params = merge(defParams, {
|
||||||
|
query: `${query} 表情`,
|
||||||
|
start: (page - 1) * size,
|
||||||
|
xml_len: size
|
||||||
|
})
|
||||||
|
const queryURL = `${api}?${serialize(params)}`
|
||||||
|
return axios.get(queryURL, { withCredentials: true }).then(({ data = {} }) => {
|
||||||
|
console.log(data)
|
||||||
|
return {
|
||||||
|
data: (data.items || []).map(it => ({
|
||||||
|
link: it.locImageLink,
|
||||||
|
suffix: it.type
|
||||||
|
})),
|
||||||
|
total: data.totalNum || 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchImgToBase64 (url) {
|
||||||
|
return axios.get(url, { responseType: 'blob' })
|
||||||
|
.then(({ data }) => new Promise((resolve, reject) => {
|
||||||
|
const reader = new window.FileReader()
|
||||||
|
reader.onloadend = () => resolve(reader.result)
|
||||||
|
reader.onerror = reject
|
||||||
|
reader.readAsDataURL(data)
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
sm (base64) {
|
||||||
|
const api = 'https://sm.ms/api/upload'
|
||||||
|
const data = new window.FormData()
|
||||||
|
data.append('smfile', dataURItoBlob(base64, 'temp.png'))
|
||||||
|
return axios.post(api, data).then(({ data }) => {
|
||||||
|
const { data: res } = data
|
||||||
|
return {
|
||||||
|
url: res.url,
|
||||||
|
err: '',
|
||||||
|
server: 'sm'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default resource
|
17
src/renderer/util/index.js
Normal file
17
src/renderer/util/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export function serialize (params) {
|
||||||
|
return Object.keys(params).map(key => `${key}=${params[key]}`).join('&')
|
||||||
|
}
|
||||||
|
export function merge (...args) {
|
||||||
|
return Object.assign({}, ...args)
|
||||||
|
}
|
||||||
|
export function dataURItoBlob (dataURI) {
|
||||||
|
const data = dataURI.split(';base64,')
|
||||||
|
const byte = window.atob(data[1])
|
||||||
|
const mime = data[0].split(':')[1]
|
||||||
|
const ab = new ArrayBuffer(byte.length)
|
||||||
|
const ia = new Uint8Array(ab)
|
||||||
|
for (let i = 0; i < byte.length; i++) {
|
||||||
|
ia[i] = byte.charCodeAt(i)
|
||||||
|
}
|
||||||
|
return new window.Blob([ab], { type: mime })
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user