Skip to content

Commit d0f8b5e

Browse files
committed
Merge branch 'dev' of https://gitee.com/dhb52/yudao-ui-admin-vue3 into dev
� Conflicts: � src/views/mp/components/wx-material-select/main.vue
2 parents e1a2d4b + be95015 commit d0f8b5e

File tree

10 files changed

+2085
-25
lines changed

10 files changed

+2085
-25
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<script setup>
2+
import { ref, reactive } from 'vue'
3+
import { QuillEditor } from '@vueup/vue-quill'
4+
import '@vueup/vue-quill/dist/vue-quill.snow.css'
5+
import { getAccessToken } from '@/utils/auth'
6+
import editorOptions from './quill-options'
7+
8+
const BASE_URL = import.meta.env.VITE_BASE_URL
9+
10+
const message = useMessage()
11+
12+
const props = defineProps({
13+
/* 公众号账号编号 */
14+
accountId: {
15+
type: Number,
16+
required: true
17+
},
18+
/* 编辑器的内容 */
19+
value: {
20+
type: String,
21+
default: ''
22+
},
23+
/* 图片大小 */
24+
maxSize: {
25+
type: Number,
26+
default: 4000 // kb
27+
}
28+
})
29+
30+
const emit = defineEmits(['input'])
31+
32+
const myQuillEditorRef = ref()
33+
34+
const content = ref(props.value.replace(/data-src/g, 'src'))
35+
36+
const loading = ref(false) // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示
37+
38+
const actionUrl = ref(BASE_URL + '/admin-api/mp/material/upload-news-image') // 这里写你要上传的图片服务器地址
39+
const headers = ref({ Authorization: 'Bearer ' + getAccessToken() }) // 设置上传的请求头部
40+
const uploadData = reactive({
41+
type: 'image', // TODO 芋艿:试试要不要换成 thumb
42+
accountId: props.accountId
43+
})
44+
45+
const onEditorChange = () => {
46+
//内容改变事件
47+
emit('input', content.value)
48+
}
49+
50+
// 富文本图片上传前
51+
const beforeUpload = () => {
52+
// 显示 loading 动画
53+
loading.value = true
54+
}
55+
56+
// 图片上传成功
57+
// 注意!由于微信公众号的图片有访问限制,所以会显示“此图片来自微信公众号,未经允许不可引用”
58+
const uploadSuccess = (res) => {
59+
// res为图片服务器返回的数据
60+
// 获取富文本组件实例
61+
const quill = myQuillEditorRef.value.quill
62+
// 如果上传成功
63+
const link = res.data
64+
if (link) {
65+
// 获取光标所在位置
66+
let length = quill.getSelection().index
67+
// 插入图片 res.info为服务器返回的图片地址
68+
quill.insertEmbed(length, 'image', link)
69+
// 调整光标到最后
70+
quill.setSelection(length + 1)
71+
} else {
72+
message.error('图片插入失败')
73+
}
74+
// loading 动画消失
75+
loading.value = false
76+
}
77+
78+
// 富文本图片上传失败
79+
const uploadError = () => {
80+
// loading 动画消失
81+
loading.value = false
82+
message.error('图片插入失败')
83+
}
84+
</script>
85+
86+
<template>
87+
<div id="wxEditor">
88+
<div v-loading="loading" element-loading-text="请稍等,图片上传中">
89+
<!-- 图片上传组件辅助-->
90+
<el-upload
91+
class="avatar-uploader"
92+
name="file"
93+
:action="actionUrl"
94+
:headers="headers"
95+
:show-file-list="false"
96+
:data="uploadData"
97+
:on-success="uploadSuccess"
98+
:on-error="uploadError"
99+
:before-upload="beforeUpload"
100+
/>
101+
<QuillEditor
102+
class="editor"
103+
v-model="content"
104+
ref="quillEditorRef"
105+
:options="editorOptions"
106+
@change="onEditorChange($event)"
107+
/>
108+
</div>
109+
</div>
110+
</template>
111+
112+
<style>
113+
.editor {
114+
line-height: normal !important;
115+
height: 500px;
116+
}
117+
118+
.ql-snow .ql-tooltip[data-mode='link']::before {
119+
content: '请输入链接地址:';
120+
}
121+
122+
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
123+
border-right: 0;
124+
content: '保存';
125+
padding-right: 0;
126+
}
127+
128+
.ql-snow .ql-tooltip[data-mode='video']::before {
129+
content: '请输入视频地址:';
130+
}
131+
132+
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
133+
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
134+
content: '14px';
135+
}
136+
137+
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='small']::before,
138+
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='small']::before {
139+
content: '10px';
140+
}
141+
142+
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='large']::before,
143+
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='large']::before {
144+
content: '18px';
145+
}
146+
147+
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='huge']::before,
148+
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='huge']::before {
149+
content: '32px';
150+
}
151+
152+
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
153+
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
154+
content: '文本';
155+
}
156+
157+
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='1']::before,
158+
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {
159+
content: '标题1';
160+
}
161+
162+
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='2']::before,
163+
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {
164+
content: '标题2';
165+
}
166+
167+
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='3']::before,
168+
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {
169+
content: '标题3';
170+
}
171+
172+
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='4']::before,
173+
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {
174+
content: '标题4';
175+
}
176+
177+
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='5']::before,
178+
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {
179+
content: '标题5';
180+
}
181+
182+
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='6']::before,
183+
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {
184+
content: '标题6';
185+
}
186+
187+
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
188+
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
189+
content: '标准字体';
190+
}
191+
192+
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='serif']::before,
193+
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='serif']::before {
194+
content: '衬线字体';
195+
}
196+
197+
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='monospace']::before,
198+
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='monospace']::before {
199+
content: '等宽字体';
200+
}
201+
</style>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const toolbarOptions = [
2+
['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
3+
['blockquote', 'code-block'], // 引用 代码块
4+
[{ header: 1 }, { header: 2 }], // 1、2 级标题
5+
[{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表
6+
[{ script: 'sub' }, { script: 'super' }], // 上标/下标
7+
[{ indent: '-1' }, { indent: '+1' }], // 缩进
8+
// [{'direction': 'rtl'}], // 文本方向
9+
[{ size: ['small', false, 'large', 'huge'] }], // 字体大小
10+
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
11+
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
12+
[{ font: [] }], // 字体种类
13+
[{ align: [] }], // 对齐方式
14+
['clean'], // 清除文本格式
15+
['link', 'image', 'video'] // 链接、图片、视频
16+
]
17+
18+
export default {
19+
theme: 'snow',
20+
placeholder: '请输入文章内容',
21+
modules: {
22+
toolbar: {
23+
container: toolbarOptions,
24+
// container: "#toolbar",
25+
handlers: {
26+
image: function (value) {
27+
if (value) {
28+
// 触发input框选择图片文件
29+
document.querySelector('.avatar-uploader input').click()
30+
} else {
31+
this.quill.format('image', false)
32+
}
33+
},
34+
link: function (value) {
35+
if (value) {
36+
const href = prompt('注意!只支持公众号图文链接')
37+
this.quill.format('link', href)
38+
} else {
39+
this.quill.format('link', false)
40+
}
41+
}
42+
}
43+
}
44+
}
45+
}

src/views/mp/components/wx-material-select/main.vue

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@
1313
<img class="material-img" :src="item.url" />
1414
<p class="item-name">{{ item.name }}</p>
1515
<el-row class="ope-row">
16-
<el-button type="success" @click="selectMaterialFun(item)">
17-
选择 <Icon icon="ep:circle-check" />
16+
<el-button type="success" @click="selectMaterialFun(item)"
17+
>选择 <Icon icon="ep:circle-check" />
1818
</el-button>
1919
</el-row>
2020
</div>
2121
</div>
2222
<!-- 分页组件 -->
23-
<Pagination
23+
<pagination
24+
v-show="total > 0"
2425
:total="total"
2526
v-model:page="queryParams.pageNo"
2627
v-model:limit="queryParams.pageSize"
@@ -29,6 +30,7 @@
2930
</div>
3031
<!-- 类型:voice -->
3132
<div v-else-if="objData.type === 'voice'">
33+
<!-- 列表 -->
3234
<el-table v-loading="loading" :data="list">
3335
<el-table-column label="编号" align="center" prop="mediaId" />
3436
<el-table-column label="文件名" align="center" prop="name" />
@@ -37,23 +39,27 @@
3739
<WxVoicePlayer :url="scope.row.url" />
3840
</template>
3941
</el-table-column>
42+
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
43+
<template #default="scope">
44+
<span>{{ formatDate(scope.row.createTime) }}</span>
45+
</template>
46+
</el-table-column>
4047
<el-table-column
41-
label="上传时间"
48+
label="操作"
4249
align="center"
43-
prop="createTime"
44-
width="180"
45-
:formatter="dateFormatter"
46-
/>
47-
<el-table-column label="操作" align="center" fixed="right">
50+
fixed="right"
51+
class-name="small-padding fixed-width"
52+
>
4853
<template #default="scope">
49-
<el-button type="text" @click="selectMaterialFun(scope.row)">
50-
选择 <Icon icon="ep:plus" />
54+
<el-button type="text" @click="selectMaterialFun(scope.row)"
55+
>选择<Icon icon="ep:plus" />
5156
</el-button>
5257
</template>
5358
</el-table-column>
5459
</el-table>
5560
<!-- 分页组件 -->
56-
<Pagination
61+
<pagination
62+
v-show="total > 0"
5763
:total="total"
5864
v-model:page="queryParams.pageNo"
5965
v-model:limit="queryParams.pageSize"
@@ -73,23 +79,27 @@
7379
<WxVideoPlayer :url="scope.row.url" />
7480
</template>
7581
</el-table-column>
82+
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
83+
<template #default="scope">
84+
<span>{{ formatDate(scope.row.createTime) }}</span>
85+
</template>
86+
</el-table-column>
7687
<el-table-column
77-
label="上传时间"
88+
label="操作"
7889
align="center"
79-
prop="createTime"
80-
width="180"
81-
:formatter="dateFormatter"
82-
/>
83-
<el-table-column label="操作" align="center">
90+
fixed="right"
91+
class-name="small-padding fixed-width"
92+
>
8493
<template #default="scope">
8594
<el-button type="text" @click="selectMaterialFun(scope.row)"
86-
>选择<Icon icon="ep:circle-plus" />
95+
>选择<Icon icon="akar-icons:circle-plus" />
8796
</el-button>
8897
</template>
8998
</el-table-column>
9099
</el-table>
91100
<!-- 分页组件 -->
92-
<Pagination
101+
<pagination
102+
v-show="total > 0"
93103
:total="total"
94104
v-model:page="queryParams.pageNo"
95105
v-model:limit="queryParams.pageSize"
@@ -104,14 +114,15 @@
104114
<WxNews :articles="item.content.newsItem" />
105115
<el-row class="ope-row">
106116
<el-button type="success" @click="selectMaterialFun(item)">
107-
选择 <Icon icon="ep:circle-check" />
117+
选择<Icon icon="ep:circle-check" />
108118
</el-button>
109119
</el-row>
110120
</div>
111121
</div>
112122
</div>
113123
<!-- 分页组件 -->
114-
<Pagination
124+
<pagination
125+
v-show="total > 0"
115126
:total="total"
116127
v-model:page="queryParams.pageNo"
117128
v-model:limit="queryParams.pageSize"
@@ -120,14 +131,15 @@
120131
</div>
121132
</div>
122133
</template>
134+
123135
<script lang="ts" name="WxMaterialSelect">
124136
import WxNews from '@/views/mp/components/wx-news/main.vue'
125137
import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue'
126138
import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue'
127139
import { getMaterialPage } from '@/api/mp/material'
128140
import { getFreePublishPage } from '@/api/mp/freePublish'
129141
import { getDraftPage } from '@/api/mp/draft'
130-
import { dateFormatter } from '@/utils/formatTime'
142+
import { dateFormatter, formatDate } from '@/utils/formatTime'
131143
import { defineComponent, PropType } from 'vue'
132144
133145
export default defineComponent({
@@ -231,6 +243,7 @@ export default defineComponent({
231243
selectMaterialFun,
232244
getMaterialPageFun,
233245
getPage,
246+
formatDate,
234247
newsTypeRef,
235248
queryParams,
236249
objDataRef,

0 commit comments

Comments
 (0)