Files
t/pages2/editCms/editCms.vue
T

847 lines
20 KiB
Vue

<template>
<view class="b_col_eee body">
<uni-nav-bar
leftText="取消"
left-icon="left"
right-text="下一步"
backgroundColor="#ffffff"
color="#000000"
@clickRight="toEditSetting()"
@clickLeft="back()">
</uni-nav-bar>
<uni-popup
ref="alertDialog"
type="dialog">
<uni-popup-dialog
type="info"
cancelText="放弃草稿"
confirmText="编辑草稿"
title="提示"
content="文章有草稿未完成,是否继续编辑草稿?"
@confirm="dialogConfirm"
@close="dialogClose">
</uni-popup-dialog>
</uni-popup>
<uni-popup ref="del_popup" type='bottom'>
<view class="del-popup">
<view class="margin_20_top padding_20 color-666 b_col_fff">
确认要删除当前文章段落
</view>
<view class="margin_4 padding_20 color-red b_col_fff" @click="delItem()">
<uni-icons type="trash" size="30" color="#f00"></uni-icons>
删除
</view>
<view class="text-center padding_20 margin_10_top b_col_fff" @click="close_del_popup()">
取消
</view>
</view>
</uni-popup>
<uni-popup ref="popup" type='bottom'>
<view class="b_col_fff">
<view class="grid-container">
<view v-for="(item, index) in cmsType" :key="index" :class="{
'grid-item': true,
}" @click="addItem(item.type)">
<view class="grid-item-box">
<image :src="item.src" class="image" mode="aspectFill" />
<text class="text">
{{ item.text }}
</text>
</view>
</view>
</view>
</view>
</uni-popup>
<view
@click="toTitleStyle()"
class="margin_50 b_col_fff padding_10"
style="display: flex; align-items: center; gap: 20rpx;">
<uni-easyinput
:inputBorder="false"
disabled
v-model="cms.title"
placeholder="请输入标题"
style="flex: 1;"
></uni-easyinput>
<view class="border_ff0" style="white-space: nowrap;">
<uni-icons type="gear" size="20" color="993300"></uni-icons>
标题样式
</view>
</view>
<view class="container">
<button class="add-button" @click="addEditItem(0)"></button>
</view>
<view v-for="(item, index) in cms.cmsLst" :key="index">
<view class="margin_20 b_col_fff edit_item padding_10">
<view class="part part1">
<view v-if="item.type === 'image'" @click="updateImage(index)">
<cloud-image
mode="scaleToFill"
width="100%"
height="200rpx"
v-if="item.image.src"
:src="item.image.src">
</cloud-image>
<view class="not-image" v-else>
<cloud-image
mode="scaleToFill"
width="100%"
height="150rpx"
:src="not_image">
</cloud-image>
<view class="not-font">
添加图片
</view>
</view>
</view>
<view class="video-container"
v-if="item.type === 'video'"
@click="updateVideoItem(index)">
<cloud-image
v-if="item.video.poster"
mode="scaleToFill"
width="100%"
height="200rpx"
:src="item.video.poster">
</cloud-image>
<cloud-image
class="icon-play"
mode="scaleToFill"
width="100rpx"
height="100rpx"
src="https://qnycdn.mymoyu.top/static/icon/stop.png">
</cloud-image>
</view>
<view
v-if="item.type === 'vote'">
<view class="not-image">
<view class="not-image-icon">
<cloud-image
mode="scaleToFill"
width="100%"
height="150rpx"
:src="is_vote">
</cloud-image>
</view>
<view class="not-font">
投票
</view>
</view>
</view>
</view>
<view class="part part2" @click="to(item.type, index)">
<view class="" v-if="item.type === 'vote'">
<view v-if="item.text">
{{ item.text }}
</view>
<view class="font-info" v-else>
点击添加文字
</view>
</view>
<view class="" v-else>
<view v-if="item.text">
{{ item.text }}
</view>
<view class="font-info" v-else>
点击添加文字
</view>
</view>
</view>
<view class="part part3">
<uni-icons
type="closeempty"
class="sub-area"
@click="open_del_popup(index)">
</uni-icons>
<uni-icons
type="bars"
class="sub-area">
</uni-icons>
<view class="sub-area item-no">
{{ index + 1 }}
</view>
</view>
</view>
<view class="container">
<button
class="add-button"
@click="addEditItem(index + 1)">
</button>
</view>
</view>
</view>
</view>
</template>
<script>
import { ref, reactive, onShow, onReady, onHide, onUnload } from 'vue'
import cloudImage from "@/components/cloud-image.vue"
const uniCloudStorageExtCo = uniCloud.importObject("ext-storage-co")
export default {
components: {
cloudImage
},
setup() {
const alertDialog = ref(null)
const delPopup = ref(null)
const popup = ref(null)
const saveInterval = ref(null)
const is_vote = ref("https://qnycdn.mymoyu.top/static/icon/is_vote.png")
const not_image = ref("https://qnycdn.mymoyu.top/static/icon/not_image.png")
const selectIndex = ref(0)
const cmsType = ref([
{ type: "text", text: "文字", src: "https://qnycdn.mymoyu.top/static/icon/text.png" },
{ type: "video", text: "视频", src: "https://qnycdn.mymoyu.top/static/icon/video.png" },
{ type: "image", text: "图片", src: "https://qnycdn.mymoyu.top/static/icon/image.png" },
{ type: "vote", text: "投票", src: "https://qnycdn.mymoyu.top/static/icon/vote.png" },
{ type: "title", text: "小标题", src: "https://qnycdn.mymoyu.top/static/icon/title.png" },
])
const cms = reactive({
title_html: "",
title_delta: {},
title: "",
cmsLst: []
})
onHide(() => {
clearInterval(saveInterval.value)
})
onUnload(() => {
clearInterval(saveInterval.value)
clearTimeout(saveInterval.value)
})
onReady(() => {
let storedCms = uni.getStorageSync("cms")
if (storedCms && ((storedCms.cmsLst && storedCms.cmsLst.length > 0) || storedCms.title || storedCms.title_html)) {
alertDialog.value.open()
}
saveInterval.value = setInterval(saveCms, 10000)
})
onShow(() => {
let storedCms = uni.getStorageSync("cms")
if (storedCms) {
Object.assign(cms, storedCms)
}
})
function saveCms() {
uni.setStorageSync("cms", cms)
}
function open() {
popup.value.open()
}
function close() {
popup.value.close()
}
function confirm(value) {
popup.value.close()
}
function addEditItem(index) {
selectIndex.value = index
uni.setStorageSync("selectIndex", selectIndex.value)
open()
}
function getFileInfo(path) {
const parts = path.split('/').pop()
const [filename, ...extParts] = parts.split('.')
const ext = extParts.length ? extParts.pop() : ''
return { name: filename, extname: ext }
}
function formatBytes(bytes) {
if (bytes === 0) return '0 Bytes'
const k = 1024
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
if (i >= sizes.length) return '> 1 TB'
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
async function updateImage(index) {
uni.chooseImage({
sourceType: ["album", "camera"],
count: 1,
success: async (res) => {
if (cms.cmsLst[index] && cms.cmsLst[index].image.src) {
await uniCloudStorageExtCo.deleteFile({ fileList: [cms.cmsLst[index].image.src] })
}
if (res.tempFilePaths.length > 0) {
let filePath = res.tempFilePaths[0]
let { name, extname } = getFileInfo(filePath)
const uploadFileOptionsRes = await uniCloudStorageExtCo.getUploadFileOptions({
cloudPath: `cms/image/${name}_${Math.random().toString(36).substr(2,6)}`
})
await uniCloud.uploadFile({
...uploadFileOptionsRes.uploadFileOptions,
filePath: filePath,
cloudPath: uploadFileOptionsRes.cloudPath,
success: async () => {
const res2 = {
cloudPath: uploadFileOptionsRes.cloudPath,
fileID: uploadFileOptionsRes.fileID,
fileURL: uploadFileOptionsRes.fileURL
}
const selectedMedia = { src: res2.fileID, alt: name }
cms.cmsLst[index].image.src = selectedMedia.src
},
fail: (err) => {
console.log("上传失败", err)
}
})
}
}
})
}
async function delItem() {
let index = selectIndex.value
if (index >= 0 && index < cms.cmsLst.length) {
if (cms.cmsLst[index].type === "image" && cms.cmsLst[index].image.src) {
await uniCloudStorageExtCo.deleteFile({ fileList: [cms.cmsLst[index].image.src] })
} else if (cms.cmsLst[index].type === "video" && (cms.cmsLst[index].video.src || cms.cmsLst[index].video.poster)) {
await uniCloudStorageExtCo.deleteFile({
fileList: [cms.cmsLst[index].video.src, cms.cmsLst[index].video.poster]
})
}
cms.cmsLst.splice(index, 1)
}
delPopup.value.close()
}
async function addImageItem() {
uni.chooseImage({
sourceType: ["album", "camera"],
count: 9,
success: async (res) => {
if (res.tempFilePaths.length > 0) {
for (let i = 0; i < res.tempFilePaths.length; i++) {
let filePath = res.tempFilePaths[i]
let { name, extname } = getFileInfo(filePath)
const uploadFileOptionsRes = await uniCloudStorageExtCo.getUploadFileOptions({
cloudPath: `cms/image/${name}_${Math.random().toString(36).substr(2,6)}`
})
await uniCloud.uploadFile({
...uploadFileOptionsRes.uploadFileOptions,
filePath: filePath,
cloudPath: uploadFileOptionsRes.cloudPath,
success: async () => {
const res2 = {
cloudPath: uploadFileOptionsRes.cloudPath,
fileID: uploadFileOptionsRes.fileID,
fileURL: uploadFileOptionsRes.fileURL
}
const selectedMedia = { src: res2.fileID, alt: name }
let item = {
type: "image",
text: "",
image: { src: selectedMedia.src, html: "", delta: {} }
}
cms.cmsLst.splice(selectIndex.value + i, 0, item)
close()
},
fail: (err) => {
console.log("上传失败", err)
}
})
}
}
}
})
}
function back() {
saveCms()
try {
uni.navigateBack({ delta: 1 })
} catch (e) {
uni.switchTab({ url: "/pages/index/index" })
}
}
function toeditText(index) {
if (index >= 0) {
uni.setStorageSync("selectIndex", index)
}
saveCms()
uni.navigateTo({ url: "/pages2/editCms/editText" })
}
function open_del_popup(index) {
selectIndex.value = index
delPopup.value.open()
}
function close_del_popup() {
delPopup.value.close()
}
function toTitleStyle() {
saveCms()
uni.navigateTo({ url: "/pages2/editCms/titleStyle" })
}
function toVote(index) {
if (index >= 0) {
uni.setStorageSync("selectIndex", index)
}
saveCms()
uni.navigateTo({ url: "/pages2/editCms/vote?type=edit" })
}
function to(type, index) {
if (type === 'vote') {
toVote(index)
} else {
toeditText(index)
}
}
function toEditSetting() {
saveCms()
uni.navigateTo({ url: "/pages2/editCms/editSetting" })
}
function addTextItem() {
cms.cmsLst.splice(selectIndex.value, 0, {
type: "text",
text: "",
image: { src: "", html: "", delta: {} },
html: "",
delta: {}
})
toeditText()
}
function addVoteItem() {
saveCms()
uni.navigateTo({ url: "/pages2/editCms/vote?type=add" })
}
function addTitleItem() {
saveCms()
uni.navigateTo({ url: "/pages2/editCms/smallTitle" })
}
async function dialogClose() {
let storedCms = uni.getStorageSync("cms")
if (storedCms && storedCms.cmsLst) {
for (let i = 0; i < storedCms.cmsLst.length; i++) {
if (storedCms.cmsLst[i].type === "image" && storedCms.cmsLst[i].image && storedCms.cmsLst[i].image.src) {
await uniCloudStorageExtCo.deleteFile({ fileList: [storedCms.cmsLst[i].image.src] })
} else if (storedCms.cmsLst[i].type === "video" && storedCms.cmsLst[i].video) {
let fileList = [storedCms.cmsLst[i].video.src].filter(Boolean)
if (storedCms.cmsLst[i].video.poster) fileList.push(storedCms.cmsLst[i].video.poster)
if (fileList.length) await uniCloudStorageExtCo.deleteFile({ fileList })
}
}
}
await uni.setStorageSync("cms", { cmsLst: [], title: "", title_html: "", title_delta: {} })
Object.assign(cms, uni.getStorageSync("cms"))
alertDialog.value.close()
}
async function dialogConfirm() {
alertDialog.value.close()
}
async function addItem(type) {
if (type === "text") {
addTextItem()
} else if (type === "image") {
await addImageItem()
} else if (type === "video") {
await addVideoItem()
} else if (type === "title") {
addTitleItem()
} else if (type === "vote") {
addVoteItem()
}
}
async function addVideoItem() {
uni.chooseVideo({
sourceType: ['album', 'camera'],
success: async (res) => {
let filePath = res.tempFilePath
let { name, extname } = getFileInfo(filePath)
let duration = res.duration
const uploadFileOptionsRes = await uniCloudStorageExtCo.getUploadFileOptions({
cloudPath: `cms/video/${name}_${Math.random().toString(36).substr(2,6)}`
})
await uniCloud.uploadFile({
...uploadFileOptionsRes.uploadFileOptions,
filePath: filePath,
cloudPath: uploadFileOptionsRes.cloudPath,
success: async () => {
const res2 = {
cloudPath: uploadFileOptionsRes.cloudPath,
fileID: uploadFileOptionsRes.fileID,
fileURL: uploadFileOptionsRes.fileURL
}
const selectedMedia = { src: res2.fileURL, alt: name }
let poster = "vframe/jpg/offset/0/w/440/h/264/"
let cover = res2.fileURL + "?" + poster
let item = {
type: "video",
text: "",
video: { src: selectedMedia.src, poster: cover, duration: duration, html: "", delta: {} }
}
cms.cmsLst.splice(selectIndex.value, 0, item)
close()
},
fail: (err) => {
console.log("上传失败", err)
}
})
}
})
}
async function updateVideoItem(index) {
uni.chooseVideo({
sourceType: ['album', 'camera'],
success: async (res) => {
if (cms.cmsLst[index] && cms.cmsLst[index].video.src) {
await uniCloudStorageExtCo.deleteFile({
fileList: [cms.cmsLst[index].video.src, cms.cmsLst[index].video.poster]
})
}
let filePath = res.tempFilePath
let { name, extname } = getFileInfo(filePath)
let duration = res.duration
const uploadFileOptionsRes = await uniCloudStorageExtCo.getUploadFileOptions({
cloudPath: `cms/video/${name}_${Math.random().toString(36).substr(2,6)}`
})
await uniCloud.uploadFile({
...uploadFileOptionsRes.uploadFileOptions,
filePath: filePath,
cloudPath: uploadFileOptionsRes.cloudPath,
success: async () => {
const res2 = {
cloudPath: uploadFileOptionsRes.cloudPath,
fileID: uploadFileOptionsRes.fileID,
fileURL: uploadFileOptionsRes.fileURL
}
const selectedMedia = { src: res2.fileURL, alt: name }
let poster = "vframe/jpg/offset/0/w/440/h/264/"
let cover = res2.fileURL + "?" + poster
let { text, html, delta } = {
text: cms.cmsLst[index].text,
html: cms.cmsLst[index].video.html,
delta: cms.cmsLst[index].video.delta
}
let item = {
type: "video",
text: text,
video: { src: selectedMedia.src, poster: cover, duration: duration, html: html, delta: delta }
}
cms.cmsLst[index] = item
close()
},
fail: (err) => {
console.log("上传失败", err)
}
})
}
})
}
return {
alertDialog,
delPopup,
popup,
saveInterval,
is_vote,
not_image,
selectIndex,
cmsType,
cms,
saveCms,
open,
close,
confirm,
addEditItem,
getFileInfo,
formatBytes,
updateImage,
delItem,
addImageItem,
back,
toeditText,
open_del_popup,
close_del_popup,
toTitleStyle,
toVote,
to,
toEditSetting,
addTextItem,
addVoteItem,
addTitleItem,
dialogClose,
dialogConfirm,
addItem,
addVideoItem,
updateVideoItem
}
}
}
</script>
<style lang="scss" scoped>
.b_col_fff {
background-color: #fff;
}
.b_col_eee {
background-color: #eee;
}
.body {
width: 100vw;
min-height: 100vh;
}
.margin_20 {
margin-top: 20rpx;
margin-bottom: 20rpx;
}
.margin_10_top {
margin-top: 10rpx;
}
.margin_10 {
margin-top: 10rpx;
margin-bottom: 10rpx;
}
.container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.add-button {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
border: 2rpx solid #ccc;
background-color: #eee;
cursor: pointer;
position: relative;
transition: transform 0.3s ease;
}
.add-button::before,
.add-button::after {
content: '';
position: absolute;
background-color: #ccc;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.add-button::before {
width: 24rpx;
height: 4rpx;
}
.color-red {
color: #f00;
}
.add-button::after {
width: 4rpx;
height: 24rpx;
}
.add-button:active {
transform: scale(0.95);
}
.edit_item {
width: 100wv;
height: 200rpx;
display: flex;
.part {
height: 100%;
&.part1 {
flex: 0 0 30%;
}
&.part2 {
flex: 0 0 63%;
display: flex;
flex-direction: column;
}
&.part3 {
flex: 0 0 7%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.sub-area {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.item-no {
background-color: #ccc;
color: #fff;
text-align: center;
width: 1.5rem;
height: 1.5rem;
}
}
}
}
.margin_20_top {
margin-top: 20rpx;
}
.padding_10 {
padding: 20rpx;
}
.padding_20 {
padding: 20rpx;
}
.font-info {
color: #666;
}
.grid-container {
.grid-item {
width: calc(98% / 3);
}
display: flex;
flex-wrap: wrap;
box-sizing: border-box;
padding: 10rpx;
width: 100%;
}
.grid-item-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
justify-content: center;
padding: 15px 0;
}
.margin_4 {
margin-top: 4rpx;
margin-bottom: 4rpx;
}
.image {
width: 50rpx;
height: 50rpx;
}
.not-image {
background-color: #eee;
width: 100%;
height: 200rpx;
text-align: center;
color: #fff;
flex-direction: column;
justify-content: center;
align-items: center;
.not-image-icon {
justify-content: center;
align-items: center;
text-align: center;
}
}
.not-font {
background-color: #7a7e83;
}
.margin_50 {
margin-top: 50rpx;
margin-bottom: 30rpx;
}
.del-popup {
background-color: #ccc;
border-radius: 20rpx 0rpx;
font-size: 40rpx;
}
.text-center {
text-align: center;
}
.margin_30_left {
margin-left: 30rpx;
}
.color-eee {
color: #eee;
}
.color-666 {
color: #666;
}
.border_ff0 {
border: 2rpx solid #993300;
background-color: #ff0;
border-radius: 10rpx;
padding: 10rpx;
}
.icon-play {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 50rpx;
color: rgba(255, 255, 255, 0.8);
}
.video-container {
position: relative;
width: 100%;
height: 200rpx;
}
</style>